mirror of https://github.com/kdl-org/kdl.git
wip parser
This commit is contained in:
parent
e6eb4f342b
commit
3296cc83a0
|
|
@ -5,3 +5,7 @@ description = "kat's document language"
|
||||||
authors = ["Kat Marchán <kzm@zkat.tech>"]
|
authors = ["Kat Marchán <kzm@zkat.tech>"]
|
||||||
license-file = "LICENSE.md"
|
license-file = "LICENSE.md"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nom = "6.0.1"
|
||||||
|
thiserror = "1.0.22"
|
||||||
|
|
|
||||||
|
|
@ -106,11 +106,11 @@ to jump in and give us your 2 cents!
|
||||||
## Grammar
|
## Grammar
|
||||||
|
|
||||||
```
|
```
|
||||||
document := linespace* (node (newline document)?)?
|
document := linespace* (node (newline document)? linespace*)?
|
||||||
|
|
||||||
node := identifier (node-space node-argument)* (node-space node-document)?
|
node := identifier (node-space node-argument)* (node-space node-document)? single-line-comment?
|
||||||
node-argument := prop | value
|
node-argument := prop | value
|
||||||
node-document := '{' linespace* document linespace* '}'
|
node-document := '{' document '}'
|
||||||
node-space := ws* escline ws* | ws+
|
node-space := ws* escline ws* | ws+
|
||||||
|
|
||||||
identifier := [a-zA-Z] [a-zA-Z0-9!#$%&'*+\-./:<>?@\^_|~]* | string
|
identifier := [a-zA-Z] [a-zA-Z0-9!#$%&'*+\-./:<>?@\^_|~]* | string
|
||||||
|
|
@ -136,7 +136,7 @@ binary := '0b' ('0' | '1') ('0' | '1' | '_')*
|
||||||
|
|
||||||
boolean := 'true' | 'false'
|
boolean := 'true' | 'false'
|
||||||
|
|
||||||
escline := '\\' newline
|
escline := '\\' ws* (single-line-comment | newline)
|
||||||
|
|
||||||
linespace := newline | ws | single-line-comment
|
linespace := newline | ws | single-line-comment
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
use nom::error::{ContextError, ParseError};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
#[derive(Debug, Clone, Error)]
|
||||||
|
#[error("Error parsing document. {kind}")]
|
||||||
|
pub struct Error {
|
||||||
|
pub input: String,
|
||||||
|
pub offset: usize,
|
||||||
|
pub kind: ErrorKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Error)]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
#[error("Failed to parse {0} component of semver string.")]
|
||||||
|
Context(&'static str),
|
||||||
|
#[error("Incomplete input to semver parser.")]
|
||||||
|
IncompleteInput,
|
||||||
|
#[error("An unspecified error occurred.")]
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct NodeParseError<I> {
|
||||||
|
pub(crate) input: I,
|
||||||
|
pub(crate) context: Option<&'static str>,
|
||||||
|
pub(crate) kind: Option<ErrorKind>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> ParseError<I> for NodeParseError<I> {
|
||||||
|
fn from_error_kind(input: I, _kind: nom::error::ErrorKind) -> Self {
|
||||||
|
Self {
|
||||||
|
input,
|
||||||
|
context: None,
|
||||||
|
kind: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append(_input: I, _kind: nom::error::ErrorKind, other: Self) -> Self {
|
||||||
|
other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> ContextError<I> for NodeParseError<I> {
|
||||||
|
fn add_context(_input: I, ctx: &'static str, mut other: Self) -> Self {
|
||||||
|
other.context = Some(ctx);
|
||||||
|
other
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/lib.rs
44
src/lib.rs
|
|
@ -1,7 +1,37 @@
|
||||||
#[cfg(test)]
|
use nom::combinator::all_consuming;
|
||||||
mod tests {
|
use nom::Err;
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
pub use crate::error::{Error, ErrorKind};
|
||||||
assert_eq!(2 + 2, 4);
|
pub use crate::node::Node;
|
||||||
}
|
|
||||||
}
|
mod error;
|
||||||
|
mod node;
|
||||||
|
mod parser;
|
||||||
|
|
||||||
|
pub fn parse_document<I>(input: I) -> Result<Vec<Node>, Error>
|
||||||
|
where
|
||||||
|
I: AsRef<str>,
|
||||||
|
{
|
||||||
|
let input = &input.as_ref()[..];
|
||||||
|
match all_consuming(parser::nodes)(input) {
|
||||||
|
Ok((_, arg)) => Ok(arg),
|
||||||
|
Err(err) => Err(match err {
|
||||||
|
Err::Error(e) | Err::Failure(e) => Error {
|
||||||
|
input: input.into(),
|
||||||
|
offset: e.input.as_ptr() as usize - input.as_ptr() as usize,
|
||||||
|
kind: if let Some(kind) = e.kind {
|
||||||
|
kind
|
||||||
|
} else if let Some(ctx) = e.context {
|
||||||
|
ErrorKind::Context(ctx)
|
||||||
|
} else {
|
||||||
|
ErrorKind::Other
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Err::Incomplete(_) => Error {
|
||||||
|
input: input.into(),
|
||||||
|
offset: input.len() - 1,
|
||||||
|
kind: ErrorKind::IncompleteInput,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
pub struct Node;
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::tag;
|
||||||
|
use nom::combinator::{map, opt};
|
||||||
|
use nom::multi::{many0, many1};
|
||||||
|
use nom::sequence::{delimited, preceded};
|
||||||
|
use nom::IResult;
|
||||||
|
|
||||||
|
use crate::error::{ErrorKind, NodeParseError};
|
||||||
|
use crate::node::Node;
|
||||||
|
|
||||||
|
/// `document := linespace* (node (newline document)?)?`
|
||||||
|
pub(crate) fn nodes(input: &str) -> IResult<&str, Vec<Node>, NodeParseError<&str>> {
|
||||||
|
// TODO: this is wrong
|
||||||
|
many0(node)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `node := identifier (node-space node-argument)* (node-space node-document)?`
|
||||||
|
pub(crate) fn node(input: &str) -> IResult<&str, Node, NodeParseError<&str>> {
|
||||||
|
let (input, tag) = identifier(input)?;
|
||||||
|
let (input, args) = many0(preceded(node_space, node_arg))(input)?;
|
||||||
|
let (input, children) = opt(preceded(node_space, node_children))(input)?;
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `identifier := [a-zA-Z] [a-zA-Z0-9!#$%&'*+\-./:<>?@\^_|~]* | string`
|
||||||
|
fn identifier(input: &str) -> IResult<&str, &str, NodeParseError<&str>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `node-space := ws* escline ws* | ws+`
|
||||||
|
fn node_space(input: &str) -> IResult<&str, (), NodeParseError<&str>> {
|
||||||
|
alt((
|
||||||
|
delimited(many0(whitespace), escline, many0(whitespace)),
|
||||||
|
map(many1(whitespace), |_| ()),
|
||||||
|
))(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `single-line-comment := '//' ('\r' [^\n] | [^\r\n])* newline`
|
||||||
|
fn single_line_comment(input: &str) -> IResult<&str, &str, NodeParseError<&str>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `multi-line-comment := '/*' ('*' [^\/] | [^*])* '*/'`
|
||||||
|
fn multi_line_comment(input: &str) -> IResult<&str, &str, NodeParseError<&str>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `escline := '\\' ws* (single-line-comment | newline)`
|
||||||
|
fn escline(input: &str) -> IResult<&str, (), NodeParseError<&str>> {
|
||||||
|
let (input, _) = tag("\\")(input)?;
|
||||||
|
let (input, _) = many0(whitespace)(input)?;
|
||||||
|
let (input, _) = alt((single_line_comment, newline))(input)?;
|
||||||
|
Ok((input, ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `ws := bom | ' ' | '\t' | multi-line-comment`
|
||||||
|
fn whitespace(input: &str) -> IResult<&str, &str, NodeParseError<&str>> {
|
||||||
|
// TODO: bom?
|
||||||
|
alt((/*bom,*/ tag(" "), tag("\t"), multi_line_comment))(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `newline := ('\r' '\n') | '\n'`
|
||||||
|
fn newline(input: &str) -> IResult<&str, &str, NodeParseError<&str>> {
|
||||||
|
alt((tag("\r\n"), tag("\n")))(input)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue