Implement Perchance grammar evaluation with basic lists
This commit is contained in:
parent
9c484e6bc1
commit
8a4963d5e9
|
@ -8,3 +8,4 @@ license = "MIT"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rand = "0.7.3"
|
||||
|
|
|
@ -6,7 +6,7 @@ Purrchance is an unofficial Rust implementation of the
|
|||
## Features I might implement eventually
|
||||
|
||||
- [ ] Parsing grammars from text format
|
||||
- [ ] Basic lists
|
||||
- [x] Basic lists
|
||||
- [ ] Probability weights
|
||||
- [ ] Single-item lists
|
||||
- [ ] Escape sequences
|
||||
|
|
104
src/lib.rs
104
src/lib.rs
|
@ -1,7 +1,107 @@
|
|||
extern crate rand;
|
||||
|
||||
use rand::{seq::SliceRandom, thread_rng};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub trait Purrchance {
|
||||
fn eval(&self, g: &Grammar) -> Option<String>;
|
||||
}
|
||||
|
||||
pub enum Symbol {
|
||||
Terminal(String),
|
||||
NonTerminal(String),
|
||||
}
|
||||
|
||||
impl Purrchance for Symbol {
|
||||
fn eval(&self, g: &Grammar) -> Option<String> {
|
||||
match self {
|
||||
Symbol::Terminal(s) => Some(s.to_string()),
|
||||
Symbol::NonTerminal(label) => g.0.get(label)?.eval(g),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Expr(Vec<Symbol>);
|
||||
|
||||
impl Purrchance for Expr {
|
||||
fn eval(&self, g: &Grammar) -> Option<String> {
|
||||
Some(self.0.iter().map(|sym| sym.eval(g)).collect::<Option<Vec<String>>>()?.join(""))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct List(Vec<Expr>);
|
||||
|
||||
impl Purrchance for List {
|
||||
fn eval(&self, g: &Grammar) -> Option<String> {
|
||||
self.0.choose(&mut thread_rng())?.eval(g)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Grammar(HashMap<String,List>);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert_eq!(2 + 2, 4);
|
||||
fn eval_terminal() {
|
||||
let sym = Symbol::Terminal("hello world".to_string());
|
||||
let g = Grammar(HashMap::new());
|
||||
assert_eq!(sym.eval(&g), Some("hello world".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_terminals_expr() {
|
||||
let sym1 = Symbol::Terminal("hell".to_string());
|
||||
let sym2 = Symbol::Terminal("o world".to_string());
|
||||
let expr = Expr(vec![sym1, sym2]);
|
||||
let g = Grammar(HashMap::new());
|
||||
assert_eq!(expr.eval(&g), Some("hello world".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_single_terminals_expr_list() {
|
||||
let sym1 = Symbol::Terminal("hell".to_string());
|
||||
let sym2 = Symbol::Terminal("o world".to_string());
|
||||
let expr = Expr(vec![sym1, sym2]);
|
||||
let list = List(vec![expr]);
|
||||
let g = Grammar(HashMap::new());
|
||||
assert_eq!(list.eval(&g), Some("hello world".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_multiple_terminals_expr_list() {
|
||||
let sym1 = Symbol::Terminal("hello".to_string());
|
||||
let sym2 = Symbol::Terminal("goodbye".to_string());
|
||||
let list = List(vec![Expr(vec![sym1]), Expr(vec![sym2])]);
|
||||
let g = Grammar(HashMap::new());
|
||||
assert!(vec![Some("hello".to_string()), Some("goodbye".to_string())].contains(&list.eval(&g)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_empty_list() {
|
||||
let list = List(vec![]);
|
||||
let g = Grammar(HashMap::new());
|
||||
assert_eq!(list.eval(&g), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_valid_nonterminal() {
|
||||
let term = Symbol::Terminal("hello world".to_string());
|
||||
let list = List(vec![Expr(vec![term])]);
|
||||
let mut g = Grammar(HashMap::new());
|
||||
g.0.insert("output".to_string(), list);
|
||||
let nt = Symbol::NonTerminal("output".to_string());
|
||||
assert_eq!(nt.eval(&g), Some("hello world".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_missing_nonterminal() {
|
||||
let term = Symbol::Terminal("hello world".to_string());
|
||||
let list = List(vec![Expr(vec![term])]);
|
||||
let mut g = Grammar(HashMap::new());
|
||||
g.0.insert("output".to_string(), list);
|
||||
let nt = Symbol::NonTerminal("missing".to_string());
|
||||
assert_eq!(nt.eval(&g), None);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue