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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
@ -168,6 +174,27 @@ dependencies = [
|
|||||||
"serde",
|
"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]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.79"
|
version = "2.0.79"
|
||||||
@ -192,4 +219,5 @@ dependencies = [
|
|||||||
"rstest",
|
"rstest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"snafu",
|
||||||
]
|
]
|
||||||
|
@ -6,6 +6,7 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
|
snafu = "0.8.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rstest = { version = "0.23.0", default-features = false }
|
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.
|
/// The definition of a standard directive.
|
||||||
///
|
///
|
||||||
/// Note that this type is an input to the parser, not an output.
|
/// 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.
|
/// Types expected by directive arguments.
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
/// Arbitrary text.
|
/// Arbitrary text.
|
||||||
Text,
|
Text,
|
||||||
@ -60,14 +71,14 @@ mod serial {
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||||
struct Document {
|
pub struct Document {
|
||||||
types: Vec<Type>,
|
pub types: Vec<Type>,
|
||||||
directives: Vec<Directive>,
|
pub directives: Vec<Directive>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "snake_case", deny_unknown_fields, tag = "kind")]
|
#[serde(rename_all = "snake_case", deny_unknown_fields, tag = "kind")]
|
||||||
enum TypeKind {
|
pub enum TypeKind {
|
||||||
Text,
|
Text,
|
||||||
Speech,
|
Speech,
|
||||||
Character,
|
Character,
|
||||||
@ -83,39 +94,172 @@ mod serial {
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||||
struct Type {
|
pub struct Type {
|
||||||
name: String,
|
pub name: String,
|
||||||
doc: String,
|
pub doc: String,
|
||||||
kind: TypeKind,
|
pub kind: TypeKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||||
struct Param {
|
pub struct Param {
|
||||||
name: String,
|
pub name: String,
|
||||||
doc: Option<String>,
|
pub doc: Option<String>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
typ: String,
|
pub typ: String,
|
||||||
nuance: Option<Box<Param>>,
|
pub nuance: Option<Box<Param>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||||
struct Directive {
|
pub struct Directive {
|
||||||
name: String,
|
pub name: String,
|
||||||
doc: String,
|
pub doc: Option<String>,
|
||||||
prefix: String,
|
pub prefix: String,
|
||||||
prefix_param: Option<Param>,
|
pub prefix_param: Option<Param>,
|
||||||
infix: Option<String>,
|
pub infix: Option<String>,
|
||||||
infix_param: Option<Param>,
|
pub infix_param: Option<Param>,
|
||||||
suffix: Option<String>,
|
pub suffix: Option<String>,
|
||||||
suffix_param: Option<Param>,
|
pub suffix_param: Option<Param>,
|
||||||
list_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");
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user