Skip to content

Commit

Permalink
Merge pull request #2 from Cirru/tests
Browse files Browse the repository at this point in the history
adding tests
  • Loading branch information
soyaine authored Apr 16, 2021
2 parents c5b38b1 + a2cba26 commit 1c904df
Show file tree
Hide file tree
Showing 6 changed files with 482 additions and 21 deletions.
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
[package]
name = "cirru_edn"
version = "0.0.1"
version = "0.0.3"
authors = ["jiyinyiyong <[email protected]>"]
edition = "2018"
license = "MIT"
description = "Parser/Writer for Cirru EDN"
homepage = "http://cirru.org"
documentation = "https://docs.rs/crate/cirru_edn/"
repository = "https://github.com/Cirru/cirru_edn.rs"
readme = "README.md"

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

[dependencies]
cirru_parser = "0.0.6"
regex = "1.4.5"
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@

### Usages

_TODO_
```bash
cargo add cirru_edn
```

```rs
use cirru_edn::{parse_cirru_edn, write_cirru_edn, CirruEdn};

parse_cirru_edn(String::from("[] 1 2 true")); // Result<CirruEdn, String>

write_cirru_edn(data); // String
```

### License

Expand Down
229 changes: 222 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,230 @@
mod primes;

use cirru_parser::{parse, write_cirru};
use primes::CirruEdn;
use cirru_parser::{parse, write_cirru, CirruNode, CirruWriterOptions};
pub use primes::CirruEdn;
use primes::CirruEdn::*;
use regex::Regex;
use std::collections::HashMap;
use std::collections::HashSet;

/// TODO
pub fn parse_cirru_edn(s: String) -> CirruEdn {
CirruEdnNil
use cirru_parser::CirruNode::*;

/// parse Cirru code into data
pub fn parse_cirru_edn(s: String) -> Result<CirruEdn, String> {
match parse(s) {
Ok(nodes) => match nodes {
CirruLeaf(_) => Err(String::from("Expected exprs")),
CirruList(xs) => {
if xs.len() == 1 {
match xs[0] {
CirruLeaf(_) => Err(String::from("Expected expr for data")),
CirruList(_) => extract_cirru_edn(&xs[0]),
}
} else {
Err(String::from("Expected 1 expr for edn"))
}
}
},
Err(e) => Err(e),
}
}

fn extract_cirru_edn(node: &CirruNode) -> Result<CirruEdn, String> {
match node {
CirruLeaf(s) => match s.as_str() {
"nil" => Ok(CirruEdnNil),
"true" => Ok(CirruEdnBool(true)),
"false" => Ok(CirruEdnBool(false)),
"" => Err(String::from("Empty string is invalid")),
s1 => match s1.chars().nth(0).unwrap() {
'\'' => Ok(CirruEdnSymbol(String::from(&s1[1..]))),
':' => Ok(CirruEdnKeyword(String::from(&s1[1..]))),
'"' | '|' => Ok(CirruEdnString(String::from(&s1[1..]))),
_ => {
if matches_float(s1) {
let f: f32 = s1.parse().unwrap();
Ok(CirruEdnNumber(f))
} else {
Err(format!("Unknown token: {:?}", s1))
}
}
},
},
CirruList(xs) => {
if xs.len() == 0 {
Err(String::from("empty expr is invalid"))
} else {
match &xs[0] {
CirruLeaf(s) => match s.as_str() {
"quote" => {
if xs.len() == 2 {
Ok(CirruEdnQuote(xs[1].clone()))
} else {
Err(String::from("Missing quote value"))
}
}
"do" => {
if xs.len() == 2 {
extract_cirru_edn(&xs[1])
} else {
Err(String::from("Missing do value"))
}
}
"[]" => {
let mut ys: Vec<CirruEdn> = vec![];
for (idx, x) in xs.iter().enumerate() {
if idx > 0 {
match extract_cirru_edn(x) {
Ok(v) => ys.push(v),
Err(v) => return Err(v),
}
}
}
Ok(CirruEdnList(ys))
}
"#{}" => {
let mut ys: HashSet<CirruEdn> = HashSet::new();
for (idx, x) in xs.iter().enumerate() {
if idx > 0 {
match extract_cirru_edn(x) {
Ok(v) => {
ys.insert(v);
}
Err(v) => return Err(v),
}
}
}
Ok(CirruEdnSet(ys))
}
"{}" => {
let mut zs: HashMap<CirruEdn, CirruEdn> = HashMap::new();
for (idx, x) in xs.iter().enumerate() {
if idx > 0 {
match x {
CirruLeaf(s) => return Err(format!("Invalid map entry: {}", s)),
CirruList(ys) => {
if ys.len() == 2 {
match (extract_cirru_edn(&ys[0]), extract_cirru_edn(&ys[1])) {
(Ok(k), Ok(v)) => {
zs.insert(k, v);
}
(e1, e2) => return Err(format!("invalid map entry: {:?} {:?}", e1, e2)),
}
}
}
}
}
}
Ok(CirruEdnMap(zs))
}
"%{}" => {
if xs.len() >= 3 {
let name = match xs[1].clone() {
CirruLeaf(s) => s,
CirruList(e) => return Err(format!("expected record name in string: {:?}", e)),
};
let mut fields: Vec<String> = vec![];
let mut values: Vec<CirruEdn> = vec![];
for (idx, x) in xs.iter().enumerate() {
if idx > 1 {
match x {
CirruLeaf(s) => return Err(format!("Invalid record entry: {}", s)),
CirruList(ys) => {
if ys.len() == 2 {
match (&ys[0], extract_cirru_edn(&ys[1])) {
(CirruLeaf(s), Ok(v)) => {
fields.push(s.clone());
values.push(v);
}
(e1, e2) => {
return Err(format!("invalid map entry: {:?} {:?}", e1, e2))
}
}
}
}
}
}
}
Ok(CirruEdnRecord(name, fields, values))
} else {
Err(format!("Not enough items for record"))
}
}
a => Err(format!("Invalid operator: {}", a)),
},
CirruList(a) => Err(format!("Invalid operator: {:?}", a)),
}
}
}
}
}

fn matches_float(x: &str) -> bool {
let re = Regex::new("^-?[\\d]+(\\.[\\d]+)?$").unwrap(); // TODO special cases not handled
re.is_match(x)
}

fn assemble_cirru_node(data: &CirruEdn) -> CirruNode {
match data {
CirruEdnNil => CirruLeaf(String::from("nil")),
CirruEdnBool(v) => CirruLeaf(format!("{}", v)),
CirruEdnNumber(n) => CirruLeaf(format!("{}", n)),
CirruEdnSymbol(s) => CirruLeaf(format!("'{}", s)),
CirruEdnKeyword(s) => CirruLeaf(format!(":{}", s)),
CirruEdnString(s) => CirruLeaf(format!("|{}", s)),
CirruEdnQuote(v) => CirruList(vec![CirruLeaf(String::from("quote")), (*v).clone()]),
CirruEdnList(xs) => {
let mut ys: Vec<CirruNode> = vec![CirruLeaf(String::from("[]"))];
for x in xs {
ys.push(assemble_cirru_node(x));
}
return CirruList(ys);
}
CirruEdnSet(xs) => {
let mut ys: Vec<CirruNode> = vec![CirruLeaf(String::from("#{}"))];
for x in xs {
ys.push(assemble_cirru_node(x));
}
return CirruList(ys);
}
CirruEdnMap(xs) => {
let mut ys: Vec<CirruNode> = vec![CirruLeaf(String::from("{}"))];
for (k, v) in xs {
ys.push(CirruList(vec![
assemble_cirru_node(k),
assemble_cirru_node(v),
]))
}
CirruList(ys)
}
CirruEdnRecord(name, fields, values) => {
let mut ys: Vec<CirruNode> = vec![
CirruLeaf(String::from("%{}")),
CirruLeaf(String::from(name)),
];
for idx in 0..fields.len() {
let v = &values[idx];
ys.push(CirruList(vec![
CirruLeaf(fields[idx].clone()),
assemble_cirru_node(v),
]));
}

CirruList(ys)
}
}
}

/// TODO
/// generate string fro, CirruEdn
pub fn write_cirru_edn(data: CirruEdn) -> String {
String::from("TODO")
let options = CirruWriterOptions { use_inline: true };
match assemble_cirru_node(&data) {
CirruLeaf(s) => write_cirru(
CirruList(vec![
(CirruList(vec![CirruLeaf(String::from("do")), CirruLeaf(s)])),
]),
options,
),
CirruList(xs) => write_cirru(CirruList(vec![(CirruList(xs))]), options),
}
}
27 changes: 19 additions & 8 deletions src/primes.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use cirru_parser::CirruNode;
use core::cmp::Ord;
use regex::Regex;
use std::cmp::Eq;
use std::cmp::Ordering;
use std::cmp::Ordering::*;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::hash::{Hash, Hasher};

#[derive(fmt::Debug)]
/// Data format based on subset of EDN, but in Cirru syntax.
/// different parts are quote and Record.
#[derive(fmt::Debug, Clone)]
pub enum CirruEdn {
CirruEdnNil,
CirruEdnBool(bool),
Expand All @@ -30,10 +33,17 @@ impl fmt::Display for CirruEdn {
CirruEdnNil => f.write_str("nil"),
CirruEdnBool(v) => f.write_str(&format!("{}", v)),
CirruEdnNumber(n) => f.write_str(&format!("{}", n)),
CirruEdnSymbol(s) => f.write_str(&format!("\"|{}\"", s)), // TODO
CirruEdnSymbol(s) => f.write_str(&format!("'{}", s)),
CirruEdnKeyword(s) => f.write_str(&format!(":{}", s)),
CirruEdnString(s) => f.write_str(&format!("'{}", s)),
CirruEdnQuote(v) => f.write_str(&format!("{}", v)),
CirruEdnString(s) => {
let re = Regex::new("^[\\d\\w\\-\\?\\.\\$,]+$").unwrap();
if re.is_match(s) {
f.write_str(&format!("|{}", s))
} else {
f.write_str(&format!("\"|{}\"", s))
}
}
CirruEdnQuote(v) => f.write_str(&format!("(quote {})", v)),
CirruEdnList(xs) => {
f.write_str("([]")?;
for x in xs {
Expand All @@ -49,10 +59,11 @@ impl fmt::Display for CirruEdn {
f.write_str(")")
}
CirruEdnMap(xs) => {
f.write_str("({}")?;
for (k, v) in xs {
f.write_str(&format!("{} {}", k, v))?;
f.write_str(&format!(" ({} {})", k, v))?;
}
Ok(())
f.write_str(")")
}
CirruEdnRecord(name, fields, values) => {
f.write_str(&format!("(%{{}} {}", name))?;
Expand Down Expand Up @@ -179,12 +190,12 @@ impl Ord for CirruEdn {
(_, CirruEdnSet(_)) => Greater,

(CirruEdnMap(a), CirruEdnMap(b)) => {
unreachable!("TODO maps are not cmp ed") // TODO
unreachable!(format!("TODO maps are not cmp ed {:?} {:?}", a, b)) // TODO
}
(CirruEdnMap(_), _) => Less,
(_, CirruEdnMap(_)) => Greater,

(CirruEdnRecord(name1, fields1, values1), CirruEdnRecord(name2, fields2, values2)) => {
(CirruEdnRecord(_name1, _fields1, _values1), CirruEdnRecord(_name2, _fields2, _values2)) => {
unreachable!("TODO records are not cmp ed") // TODO
}
}
Expand Down
Loading

0 comments on commit 1c904df

Please sign in to comment.