Skip to content

Commit

Permalink
feat(parser): v2 parser (#70)
Browse files Browse the repository at this point in the history
This is a major rewrite of kdl-rs to comply with the KDL v2 spec.
  • Loading branch information
zkat authored Oct 6, 2024
1 parent 6044ef9 commit c9898cd
Show file tree
Hide file tree
Showing 290 changed files with 2,960 additions and 1,290 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
rust: [1.56.0, stable]
rust: [1.70.0, stable]
os: [ubuntu-latest, macOS-latest, windows-latest]

steps:
Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ default = ["span"]
span = []

[dependencies]
miette = "5.7.0"
miette = "7.2.0"
nom = "7.1.1"
thiserror = "1.0.40"
winnow = { version = "0.6.20", features = ["alloc", "unstable-recover"] }

[dev-dependencies]
miette = { version = "5.7.0", features = ["fancy"] }
miette = { version = "7.2.0", features = ["fancy"] }
pretty_assertions = "1.3.0"
36 changes: 4 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,6 @@ assert_eq!(&doc.to_string(), node_str);
[`KdlDocument`], [`KdlNode`], [`KdlEntry`], and [`KdlIdentifier`] can all
be parsed and managed this way.

#### Query Engine

`kdl` includes a query engine for
[KQL](https://github.com/kdl-org/kdl/blob/main/QUERY-SPEC.md), which lets you
pick out nodes from a document using a CSS Selectors-style syntax.

Queries can be done from either a [`KdlDocument`] or a [`KdlNode`], with
mostly the same semantics.

```rust
use kdl::KdlDocument;

let doc = r#"
a {
b 1
c 2
d 3 {
e prop="hello"
}
}
"#.parse::<KdlDocument>().expect("failed to parse KDL");

let results = doc.query("a > b").expect("failed to parse query");
assert_eq!(results, Some(&doc.nodes()[0].children().unwrap().nodes()[0]));

let results = doc.query_get("e", "prop").expect("failed to parse query");
assert_eq!(results, Some(&"hello".into()));

let results = doc.query_get_all("a > []", 0).expect("failed to parse query").collect::<Vec<_>>();
assert_eq!(results, vec![&1.into(), &2.into(), &3.into()]);
```

### Error Reporting

[`KdlError`] implements [`miette::Diagnostic`] and can be used to display
Expand Down Expand Up @@ -154,6 +122,10 @@ means a few things:
you [`KdlDocument::fmt`] in which case the original representation will be
thrown away and the actual value will be used when serializing.

### Minimum Supported Rust Version

You must be at least `1.70.0` tall to get on this ride.

### License

The code in this repository is covered by [the Apache-2.0
Expand Down
6 changes: 3 additions & 3 deletions examples/Cargo.kdl
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package {
name "kdl"
name kdl
version "0.0.0"
description "kat's document language"
description "The kdl document language"
authors "Kat Marchán <[email protected]>"
license-file "LICENSE.md"
license-file LICENSE.md
edition "2018"
}

Expand Down
39 changes: 22 additions & 17 deletions examples/ci.kdl
Original file line number Diff line number Diff line change
@@ -1,47 +1,52 @@
// This example is a GitHub Action if it used KDL syntax.
// See .github/workflows/ci.yml for the file this was based on.
name "CI"
name CI

on "push" "pull_request"
on push pull_request

env {
RUSTFLAGS "-Dwarnings"
RUSTFLAGS -Dwarnings
}

jobs {
fmt_and_docs "Check fmt & build docs" {
runs-on "ubuntu-latest"
runs-on ubuntu-latest
steps {
step uses="actions/checkout@v1"
step "Install Rust" uses="actions-rs/toolchain@v1" {
profile "minimal"
toolchain "stable"
components "rustfmt"
override true
profile minimal
toolchain stable
components rustfmt
override #true
}
step "rustfmt" run="cargo fmt --all -- --check"
step "docs" run="cargo doc --no-deps"
step rustfmt { run cargo fmt --all -- --check }
step docs { run cargo doc --no-deps }
}
}
build_and_test "Build & Test" {
runs-on "${{ matrix.os }}"
strategy {
matrix {
rust "1.46.0" "stable"
os "ubuntu-latest" "macOS-latest" "windows-latest"
rust "1.46.0" stable
os ubuntu-latest macOS-latest windows-latest
}
}

steps {
step uses="actions/checkout@v1"
step "Install Rust" uses="actions-rs/toolchain@v1" {
profile "minimal"
profile minimal
toolchain "${{ matrix.rust }}"
components "clippy"
override true
components clippy
override #true
}
step "Clippy" run="cargo clippy --all -- -D warnings"
step "Run tests" run="cargo test --all --verbose"
step Clippy { run cargo clippy --all -- -D warnings }
step "Run tests" { run cargo test --all --verbose }
step "Other Stuff" run="
echo foo
echo bar
echo baz
"
}
}
}
87 changes: 45 additions & 42 deletions examples/custom-errors.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,50 @@
/// Show how to build your own diagnostics, without having to use the
/// `fancy` feature or having `main()` return `miette::Result`
use kdl::KdlDocument;
use miette::Diagnostic;
use miette::SourceSpan;
// TODO(@zkat): Error stuff has changed, so this needs updating.

#[derive(Debug)]
pub struct MyError {
pub message: String,
}
// /// Show how to build your own diagnostics, without having to use the
// /// `fancy` feature or having `main()` return `miette::Result`
// ///
// use kdl::KdlDocument;
// use miette::Diagnostic;
// use miette::SourceSpan;

fn parse(input: &str) -> Result<KdlDocument, MyError> {
let doc = input.parse::<KdlDocument>();
doc.map_err(|error| {
let source = error
.source_code()
.expect("parse errors should have source code");
let help = error.help.unwrap_or_default();
let span: SourceSpan = error.span;
let contents = source
.read_span(&span, 0, 0)
.expect("source should have span contents");
// miette uses 0 based indexes, but humans prefer 1-based
let line = contents.line() + 1;
let column = contents.column() + 1;
let message = format!(
"line {}, column {}: {}\n help: {}",
line, column, error, help
);
MyError { message }
})
}
// #[derive(Debug)]
// pub struct MyError {
// pub message: String,
// }

// fn parse(input: &str) -> Result<KdlDocument, MyError> {
// let doc = input.parse::<KdlDocument>();
// doc.map_err(|error| {
// let source = error
// .source_code()
// .expect("parse errors should have source code");
// let help = error.help.unwrap_or_default();
// let span: SourceSpan = error.span;
// let contents = source
// .read_span(&span, 0, 0)
// .expect("source should have span contents");
// // miette uses 0 based indexes, but humans prefer 1-based
// let line = contents.line() + 1;
// let column = contents.column() + 1;
// let message = format!(
// "line {}, column {}: {}\n help: {}",
// line, column, error, help
// );
// MyError { message }
// })
// }

fn main() {
let input = r#"
foo {
bar {
baz 1.
}
}
"#;
let err = parse(input).unwrap_err();
eprintln!("{}", err.message);
// Output:
// line 4, column 14: Expected valid value.
// help: Floating point numbers must be base 10, and have numbers after the decimal point.
// let input = r#"
// foo {
// bar {
// baz 1.
// }
// }
// "#;
// let err = parse(input).unwrap_err();
// eprintln!("{}", err.message);
// // Output:
// // line 4, column 14: Expected valid value.
// // help: Floating point numbers must be base 10, and have numbers after the decimal point.
}
2 changes: 1 addition & 1 deletion examples/insert-node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ words {
let word_node = KdlNode::new(identifier);
word_nodes.push(word_node);
word_nodes.sort_by(sort_by_name);
words_section.fmt();
words_section.autoformat();

println!("{}", doc);

Expand Down
Loading

0 comments on commit c9898cd

Please sign in to comment.