From aa0cf274a9d62f90662e60a0f96e1d0cebaaccac Mon Sep 17 00:00:00 2001
From: koalp <koalp@alpaga.dev>
Date: Sun, 20 Sep 2020 22:03:21 +0200
Subject: [PATCH] feat: add python type parsing

---
 Cargo.lock            | 182 ++++++++++++++++++++++++++++++++++++++++++
 Cargo.toml            |   3 +
 src/error.rs          |   0
 src/grammar.rs        |  48 +++++++++++
 src/grammar/type.pest |   7 ++
 src/main.rs           |  37 ++++++++-
 src/nested_type.rs    |  47 +++++++++++
 7 files changed, 322 insertions(+), 2 deletions(-)
 create mode 100644 src/error.rs
 create mode 100644 src/grammar.rs
 create mode 100644 src/grammar/type.pest
 create mode 100644 src/nested_type.rs

diff --git a/Cargo.lock b/Cargo.lock
index 97d9105..e6358f8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,5 +1,187 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+[[package]]
+name = "anyhow"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
+
+[[package]]
+name = "block-buffer"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+dependencies = [
+ "block-padding",
+ "byte-tools",
+ "byteorder",
+ "generic-array",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
+dependencies = [
+ "byte-tools",
+]
+
+[[package]]
+name = "byte-tools"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
+
+[[package]]
+name = "byteorder"
+version = "1.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
+
+[[package]]
+name = "digest"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "fake-simd"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+
+[[package]]
+name = "generic-array"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
+[[package]]
+name = "opaque-debug"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+
 [[package]]
 name = "parsing"
 version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "pest",
+ "pest_derive",
+]
+
+[[package]]
+name = "pest"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+dependencies = [
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
+dependencies = [
+ "maplit",
+ "pest",
+ "sha-1",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "sha-1"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
+dependencies = [
+ "block-buffer",
+ "digest",
+ "fake-simd",
+ "opaque-debug",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "typenum"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
diff --git a/Cargo.toml b/Cargo.toml
index e572f76..239098a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,3 +7,6 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+pest = "2.1.3"
+pest_derive = "2.1.0"
+anyhow = "1.0.32"
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..e69de29
diff --git a/src/grammar.rs b/src/grammar.rs
new file mode 100644
index 0000000..7d1b246
--- /dev/null
+++ b/src/grammar.rs
@@ -0,0 +1,48 @@
+use crate::nested_type::Type;
+use ::anyhow::Result;
+
+use pest::{iterators::Pair, Parser};
+
+#[derive(Parser)]
+#[grammar = "grammar/type.pest"]
+pub struct TypeParser;
+
+fn parse_type_array(pair: Pair<Rule>) -> Result<Vec<Box<Type>>> {
+    let mut inner_rules = pair.into_inner();
+    let types = inner_rules.map( |pair| parse_type_object(pair).unwrap_or_default().into()).collect();
+    Ok(types)
+}
+
+fn parse_type_object(pair: Pair<Rule>) -> Result<Type> {
+    // verify that it's a type object ?
+    let mut inner_rules = pair.into_inner();
+    let repr = inner_rules
+        .next()
+        .expect("Malformed parsing")
+        .as_str();
+    let subtypes = match inner_rules.next() {
+        Some(a) => parse_type_array(a)?,
+        None => vec![]
+    };
+    Ok(Type {
+        repr: repr.into(),
+        subtypes: subtypes,
+    })
+}
+
+fn parse_nested_type(pair: Pair<Rule>) -> Result<Type>{
+    match pair.as_rule() {
+        Rule::type_object => parse_type_object(pair),
+        _ => unreachable!(),
+    }
+}
+
+pub fn parse(s: String) -> Result<Type> {
+    let t = match TypeParser::parse(Rule::nested_type, s.as_str())?.next() {
+        Some(v) => v,
+        //TODO: return a custom error
+        None => panic!("nothing have been parsed"),
+    };
+
+    Ok(parse_nested_type(t)?)
+}
diff --git a/src/grammar/type.pest b/src/grammar/type.pest
new file mode 100644
index 0000000..d060d30
--- /dev/null
+++ b/src/grammar/type.pest
@@ -0,0 +1,7 @@
+nested_type = _{SOI ~ type_object ~ EOI}
+
+type_object = { type_name ~ (type_array)? }
+type_array = { "[" ~ type_object ~ ("," ~ type_object)* ~ "]" }
+
+type_name = { ( ASCII_ALPHA | ASCII_DIGIT )+ }
+WHITESPACE = _{ " " }
diff --git a/src/main.rs b/src/main.rs
index e7a11a9..b1f1f16 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,36 @@
-fn main() {
-    println!("Hello, world!");
+mod grammar;
+mod nested_type;
+
+use grammar::parse;
+use nested_type::Type;
+use ::std::env;
+
+use ::anyhow::Result;
+
+#[macro_use]
+extern crate pest_derive;
+extern crate pest;
+
+fn main() -> Result<()> {
+    let a = Type {
+        repr: "NestedType".into(),
+        ..Default::default()
+    };
+    let b = Type {
+        repr: "MainType".into(),
+        subtypes: vec![a.clone().into(), a.clone().into()],
+    };
+    println!("this is my python type : {}", b.to_python());
+    println!("this is my cpp type : {}", b.to_cpp());
+
+    let nested_type = match env::args().nth(1) {
+        Some(t) => t,
+        None => "Dict[str, int]".into(),
+    };
+    println!(
+        "I parse an awesome nested_type : {}",
+        parse(nested_type.into())?.to_python()
+    );
+
+    Ok(())
 }
diff --git a/src/nested_type.rs b/src/nested_type.rs
new file mode 100644
index 0000000..e171744
--- /dev/null
+++ b/src/nested_type.rs
@@ -0,0 +1,47 @@
+#[derive(Debug, Default, Clone)]
+pub struct Type {
+    pub subtypes: Vec<Box<Type>>,
+    pub repr: String,
+}
+
+impl Type {
+
+    /// A function to print a Type struct as a python String
+    ///
+    /// # Examples 
+    ///
+    /// Creating a simple type and converting it into a python type String
+    /// ```
+    /// let my_type = Type{repr: "Type".into(), ..Default::default()};
+    /// my_type.to_python()
+    /// ```
+    pub fn to_python(&self) -> String {
+        match self.subtypes.len() {
+            0 => format!("{}", self.repr),
+            _ => format!(
+                "{}[{}]",
+                self.repr,
+                self.subtypes
+                    .iter()
+                    .map(|s| s.to_python())
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+        }
+    }
+    
+    pub fn to_cpp(&self) -> String {
+        match self.subtypes.len() {
+            0 => format!("{}", self.repr),
+            _ => format!(
+                "{}<{}>",
+                self.repr,
+                self.subtypes
+                    .iter()
+                    .map(|s| s.to_python())
+                    .collect::<Vec<String>>()
+                    .join(", ")
+            ),
+        }
+    }
+}