directive: parse and type check definitions
This commit is contained in:
parent
fff67316bb
commit
fa257d7db2
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -23,6 +23,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
@ -168,6 +174,27 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019"
|
||||
dependencies = [
|
||||
"snafu-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snafu-derive"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.79"
|
||||
@ -192,4 +219,5 @@ dependencies = [
|
||||
"rstest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"snafu",
|
||||
]
|
||||
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
serde_json = "1.0.128"
|
||||
snafu = "0.8.5"
|
||||
|
||||
[dev-dependencies]
|
||||
rstest = { version = "0.23.0", default-features = false }
|
||||
|
190
src/directive.rs
190
src/directive.rs
@ -1,3 +1,13 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use snafu::{ResultExt, Snafu};
|
||||
|
||||
/// The types and directives in a VijiN project.
|
||||
pub struct TypeSystem {
|
||||
pub types: BTreeMap<String, Type>,
|
||||
pub directives: BTreeMap<String, Directive>,
|
||||
}
|
||||
|
||||
/// The definition of a standard directive.
|
||||
///
|
||||
/// Note that this type is an input to the parser, not an output.
|
||||
@ -39,6 +49,7 @@ pub struct Param {
|
||||
}
|
||||
|
||||
/// Types expected by directive arguments.
|
||||
#[derive(Clone)]
|
||||
pub enum Type {
|
||||
/// Arbitrary text.
|
||||
Text,
|
||||
@ -60,14 +71,14 @@ mod serial {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||
struct Document {
|
||||
types: Vec<Type>,
|
||||
directives: Vec<Directive>,
|
||||
pub struct Document {
|
||||
pub types: Vec<Type>,
|
||||
pub directives: Vec<Directive>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "snake_case", deny_unknown_fields, tag = "kind")]
|
||||
enum TypeKind {
|
||||
pub enum TypeKind {
|
||||
Text,
|
||||
Speech,
|
||||
Character,
|
||||
@ -83,39 +94,172 @@ mod serial {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||
struct Type {
|
||||
name: String,
|
||||
doc: String,
|
||||
kind: TypeKind,
|
||||
pub struct Type {
|
||||
pub name: String,
|
||||
pub doc: String,
|
||||
pub kind: TypeKind,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||
struct Param {
|
||||
name: String,
|
||||
doc: Option<String>,
|
||||
pub struct Param {
|
||||
pub name: String,
|
||||
pub doc: Option<String>,
|
||||
#[serde(rename = "type")]
|
||||
typ: String,
|
||||
nuance: Option<Box<Param>>,
|
||||
pub typ: String,
|
||||
pub nuance: Option<Box<Param>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||
struct Directive {
|
||||
name: String,
|
||||
doc: String,
|
||||
prefix: String,
|
||||
prefix_param: Option<Param>,
|
||||
infix: Option<String>,
|
||||
infix_param: Option<Param>,
|
||||
suffix: Option<String>,
|
||||
suffix_param: Option<Param>,
|
||||
list_param: Option<Param>,
|
||||
pub struct Directive {
|
||||
pub name: String,
|
||||
pub doc: Option<String>,
|
||||
pub prefix: String,
|
||||
pub prefix_param: Option<Param>,
|
||||
pub infix: Option<String>,
|
||||
pub infix_param: Option<Param>,
|
||||
pub suffix: Option<String>,
|
||||
pub suffix_param: Option<Param>,
|
||||
pub list_param: Option<Param>,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum Error {
|
||||
#[snafu(display("invalid directive definition file: {source}"))]
|
||||
Decode { source: serde_json::Error },
|
||||
|
||||
#[snafu(display("no type {name} of {position} parameter for {directive}"))]
|
||||
ParamType {
|
||||
directive: String,
|
||||
position: &'static str,
|
||||
name: String,
|
||||
},
|
||||
|
||||
#[snafu(display("{directive} has {position} parameter with no {position}"))]
|
||||
MissingSyntax {
|
||||
directive: String,
|
||||
position: &'static str,
|
||||
},
|
||||
|
||||
#[snafu(display("{directive} has illegal nuance on {position}"))]
|
||||
InvalidNuance {
|
||||
directive: String,
|
||||
position: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
static DIRECTIVE_PREDECLARED: &str = include_str!("directive-predeclared.json");
|
||||
|
||||
impl TypeSystem {
|
||||
pub fn new() -> TypeSystem {
|
||||
let mut ts = TypeSystem {
|
||||
types: BTreeMap::new(),
|
||||
directives: BTreeMap::new(),
|
||||
};
|
||||
ts.parse(DIRECTIVE_PREDECLARED).unwrap();
|
||||
ts
|
||||
}
|
||||
|
||||
pub fn parse(&mut self, src: &str) -> Result<&mut Self, Error> {
|
||||
let s: serial::Document = serde_json::from_str(src).context(DecodeSnafu)?;
|
||||
for t in s.types {
|
||||
let p = match t.kind {
|
||||
serial::TypeKind::Text => Type::Text,
|
||||
serial::TypeKind::Speech => Type::Speech,
|
||||
serial::TypeKind::Character => Type::Character,
|
||||
serial::TypeKind::Duration => Type::Duration,
|
||||
serial::TypeKind::Asset { tags } => Type::Asset { tags },
|
||||
serial::TypeKind::Enum { enm } => Type::Enum(enm),
|
||||
};
|
||||
self.types.insert(t.name, p);
|
||||
}
|
||||
for d in s.directives {
|
||||
let prefix_param = d
|
||||
.prefix_param
|
||||
.map(|p| self.param(&d.name, "prefix", p))
|
||||
.transpose()?;
|
||||
let infix_param = d
|
||||
.infix_param
|
||||
.map(|p| {
|
||||
if d.suffix.is_none() {
|
||||
return Err(Error::MissingSyntax {
|
||||
directive: d.name.clone(),
|
||||
position: "infix",
|
||||
});
|
||||
}
|
||||
self.param(&d.name, "infix", p)
|
||||
})
|
||||
.transpose()?;
|
||||
let suffix_param = d
|
||||
.suffix_param
|
||||
.map(|p| {
|
||||
if d.suffix.is_none() {
|
||||
return Err(Error::MissingSyntax {
|
||||
directive: d.name.clone(),
|
||||
position: "suffix",
|
||||
});
|
||||
}
|
||||
self.param(&d.name, "suffix", p)
|
||||
})
|
||||
.transpose()?;
|
||||
let list_param = d
|
||||
.list_param
|
||||
.map(|p| self.param(&d.name, "list", p))
|
||||
.transpose()?;
|
||||
let r = Directive {
|
||||
name: d.name.clone(),
|
||||
doc: d.doc.unwrap_or_default(),
|
||||
prefix: d.prefix,
|
||||
prefix_param,
|
||||
infix: d.infix,
|
||||
infix_param,
|
||||
suffix: d.suffix,
|
||||
suffix_param,
|
||||
list_param,
|
||||
};
|
||||
self.directives.insert(d.name, r);
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn param(
|
||||
&self,
|
||||
directive: &str,
|
||||
position: &'static str,
|
||||
p: serial::Param,
|
||||
) -> Result<Param, Error> {
|
||||
let t = match self.types.get(&p.typ) {
|
||||
None => {
|
||||
return Err(Error::ParamType {
|
||||
directive: directive.into(),
|
||||
position,
|
||||
name: p.typ,
|
||||
})
|
||||
}
|
||||
Some(t) => t,
|
||||
};
|
||||
let nuance = if let Some(n) = p.nuance {
|
||||
if position != "list" {
|
||||
return Err(Error::InvalidNuance {
|
||||
directive: directive.into(),
|
||||
position,
|
||||
});
|
||||
}
|
||||
Some(Box::new(self.param(directive, "list nuance", *n)?))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Param {
|
||||
name: p.name,
|
||||
doc: p.doc,
|
||||
typ: t.clone(),
|
||||
nuance,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
Loading…
Reference in New Issue
Block a user