Skip to content

Commit

Permalink
Initial parsing of erd files
Browse files Browse the repository at this point in the history
  • Loading branch information
Jesse Hoobergs committed Sep 16, 2021
0 parents commit 9ff4f04
Show file tree
Hide file tree
Showing 16 changed files with 540 additions and 0 deletions.
1 change: 1 addition & 0 deletions erd/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
182 changes: 182 additions & 0 deletions erd/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions erd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "erd-script"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
pest = "2.1"
pest_derive = "2.1"
89 changes: 89 additions & 0 deletions erd/src/ast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::convert::TryInto;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Ident(pub String);

impl std::convert::From<String> for Ident {
fn from(s: String) -> Self {
Ident(s)
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Attribute {
Normal(Ident),
Key(Ident),
}

impl std::convert::From<(String, String)> for Attribute {
fn from((r#type, name): (String, String)) -> Self {
let ident = name.into();
match &r#type[..] {
"id" => Self::Key(ident),
"attribute" => Self::Normal(ident),
_ => unreachable!(),
}
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RelationCardinality {
One,
Multiple,
Exact(usize),
}

impl std::convert::From<String> for RelationCardinality {
fn from(s: String) -> Self {
if s.starts_with("exactly") {
Self::Exact(s["exactly".len()..(s.len() - 1)].parse().unwrap())
} else {
match &s[..] {
"one" => Self::One,
"multiple" => Self::Multiple,
_ => unreachable!(),
}
}
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RelationOptionality {
Optional,
Required,
}

impl std::convert::From<String> for RelationOptionality {
fn from(s: String) -> Self {
match &s[..] {
"optional" => Self::Optional,
"required" => Self::Required,
_ => unreachable!(),
}
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RelationMember {
cardinality: RelationCardinality,
optionality: RelationOptionality,
entity: Ident,
}

impl std::convert::From<(String, String, String)> for RelationMember {
fn from((cardinality, optionality, entity): (String, String, String)) -> Self {
Self {
cardinality: cardinality.into(),
optionality: optionality.into(),
entity: entity.into(),
}
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Expr {
/// Matches an entity with attributes
Entity(Ident, Vec<Attribute>),
/// Matches a relation with an optional name and members
Relation(Ident, Option<String>, Vec<RelationMember>),
}
21 changes: 21 additions & 0 deletions erd/src/erd.pest
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
WHITESPACE = _{ " " | "\t" }

COMMENT = _{ "//" ~ (!"\n" ~ ANY)* }

ident = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }

attribute_prefix = { "attribute" | "id" }
attribute = { attribute_prefix ~ ident }
entity = { "entity" ~ ident ~ (!"\n\n" ~ "\n" ~ attribute)* }

relation_name = { (!")" ~ ANY)* }
relation = { "relation" ~ ident ~ ("(" ~ relation_name ~ ")")? ~ ((!"\n\n" ~ "\n") ~ relation_part)+ }

relation_part = { cardinality ~ optionality ~ ident }
cardinality = { "multiple" | "one" | ("exactly(" ~ ASCII_DIGIT+ ~ ")" ) }
optionality = { "optional" | "required" }

expression = { entity | relation }

erd = _{ SOI ~ "\n"* ~ expression ~ ("\n"{2,} ~ expression)* ~ "\n"* ~ EOI }

38 changes: 38 additions & 0 deletions erd/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
mod ast;
mod parser;

extern crate pest;
#[macro_use]
extern crate pest_derive;

use parser::ConsumeError;

fn parse_file(path: &std::path::Path) -> Result<(), ConsumeError> {
println!("{:?}", path.display());
let content = std::fs::read_to_string(path).expect("Valid file");
let pairs =
parser::parse_as_erd(&content).map_err(|e| parser::ConsumeError::ERDParseError(vec![e]))?;
println!("{:?}", pairs);
let asts = parser::consume_expressions(pairs)?;
println!("{:?}\n", asts);
Ok(())
}

fn main() {
println!("todo")
}

#[cfg(test)]
mod test {
use super::*;
#[test]
fn parse_examples() -> Result<(), ConsumeError> {
let paths = std::fs::read_dir("../examples").unwrap();

for path in paths {
parse_file(&path.unwrap().path())?;
}

Ok(())
}
}
Loading

0 comments on commit 9ff4f04

Please sign in to comment.