lichen 0.3.7

Scripting DSL (for Dialogue Graphs, et al)
Documentation
extern crate lichen;

use lichen::parse::{Parser,Block,SrcBlock,Map};
use lichen::logic::{Logic,Expect};
use lichen::var::{Var,Mut};
use lichen::source::{Src,Next};
use lichen::eval::Evaluator;

use std::collections::HashMap;


#[test]
fn parse_block() {
    let src = "root\n
    @root.some_item \"Thing\"\n
    unequipped !root.some_item\n
    @root.some_weight 4\n
    has_weight root.some_weight < 5.0\n
    some_comp:any [\nunequipped \nhas_weight\n]\n
\n
    if unequipped \"you're looking for something?\"\n
\n
    if some_comp \"welcome, \nlook around\"\n
    next:now end\n
;";
    
    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");

    let block_ = [Block::Src(
        SrcBlock {
            idx: 0,
            visited: false,
            or_valid: false,
            name: "root".to_owned(),
            src: vec![Src::Mut(Mut::Swap,"root.some_item".to_owned(),vec![Var::String("Thing".to_owned())]),

                      Src::Logic("not_root.some_item".to_owned(),
                                 Logic::IsNot("root.some_item".to_owned())),
                      Src::Logic("unequipped".to_owned(),
                                 Logic::Is("not_root.some_item".to_owned())),

                      Src::Mut(Mut::Swap,"root.some_weight".to_owned(),vec![Var::Num(4.)]),
                      
                      Src::Logic("has_weight".to_owned(),
                                 Logic::LT(Var::Sym("root.some_weight".to_owned()), 5.0 .into())),
                      Src::Logic("some_comp".to_owned(),
                                 Logic::Composite(
                                     Expect::Any,
                                     vec!["unequipped".to_owned(),"has_weight".to_owned()])),
                      Src::If("unequipped".to_owned(),
                              vec!["you're looking for something?".into()],
                              None),
                      Src::If("some_comp".to_owned(),
                              vec!["welcome, \nlook around".into()],
                              None),
                      Src::Next(Next::Now("end".to_owned()))],
            logic: HashMap::new(),
        })];
    
    assert_eq!(block[0],block_[0]);
}

#[test]
fn parse_qsym_block() {
    let src = "root\n
    if !some_item \"you're looking for something?\"\n
;";
    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    match &block[0] {
        &Block::Src(ref b) => {
            let r;
            match b.src[0] {
                Src::Logic(ref qsym,_) => { r = qsym; },
                _ => panic!("unknown source found")
            }

            match b.src[1] {
                Src::If(ref r_,_,_) => {
                    assert_eq!(r,r_);
                },
                _ => panic!("unknown source found")
            }
        },
        _ => panic!("unknown block found")
    }
}

#[test]
fn parse_qsym_comp_block() {
    let src =  "root\n
    has_weight some_weight < 5.0\n
    some_comp:any [has_weight !some_item ]\n
    ;";
    
    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");

    match &block[0] {
        &Block::Src(ref b) => {
            let r;
            match b.src[1] {
                Src::Logic(ref qsym,_) => { r = qsym; },
                _ => panic!("unknown source found")
            }

            match b.src[2] {
                Src::Logic(ref _n,ref l) => {
                    match l {
                        &Logic::Composite(ref _x, ref v) => {
                            assert_eq!(r,&v[1]);
                        },
                        _ => panic!("unknown logic found")
                    }
                },
                _ => panic!("unknown source found")
            }
        },
        _ => panic!("unknown block found")
    }
}

#[test]
fn validate_qsym_block() {
    let src =  "root\n
    @root.other_item \"thing\"\n
    if root.other_item next:await store\n
    ;";
    
    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (_,nn) = ev.next().unwrap();
    
    assert_eq!(nn, Some(Next::Await("store".into())));
}

#[test]
fn validate_reflection_block() {
    let src =  "root\n
    @root.other_item \"thing\"\n
    \n
    hasnt !root.some_item\n
    hasnt-inv !hasnt\n
    comp:all root.other_item !hasnt-inv\n
    if comp next:await store\n
    ;";
    
    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (_,nn) = ev.next().unwrap();
    
    assert_eq!(nn, Some(Next::Await("store".into())));
}

#[test]
fn parse_if_vec_block() {
    let src = "root\n
    if !some_item [\n
        \"you're looking for something?\"\n
        \"welcome, \nlook around\"\n
        next:now store]\n
;";
    
    let block = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    
    match &block[0] {
        &Block::Src(ref b) => {
            match b.src[1] {
                Src::If(_,_, ref next) => {
                    assert_eq!(next,&Some(Next::Now("store".to_owned())));
                },
                _ => panic!("unknown source found")
            }
        },
        _ => panic!("unknown block found")
    }
}

#[test]
fn parse_eval_str_block() {
    let src = "root\n
    @root.name \"Io\"\n
    @root.weight 4\n
        has_weight root.weight < 5.0\n
        some_comp:all [has_weight !some_item ]\n
    if some_comp \"looks like you are `root.weight kgs heavy, `root.name\"\n
;";
    
    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    
    let ev = Evaluator::new(&mut env);
    let (vars,_node) = ev.last().unwrap();
    
    assert_eq!(vars, ["looks like you are 4 kgs heavy, Io".into()]);
}

#[test]
fn parse_compare_env_block() {
    let src = "root\n
    weight 0 < 1\n
    if weight next:now store\n
;";
    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (_vars,node) = ev.last().unwrap();
    
    assert_eq!(node, Some(Next::Now("store".to_string())));
}

#[test]
fn parse_return_varkind() {
    let src = "root\n
    @root.name \"Io\"\n
    @root.some_weight 4\n
    @root.other_weight 5\n
    has_weight root.some_weight < root.other_weight\n
    if has_weight root.some_weight \"hi `root.name\"\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();
    
    assert_eq!(vars[0], 4.0 .into());
    assert_eq!(vars[1], "hi Io" .into());
}

#[test]
fn parse_follow_nodes() {
    let src = "root\n
    weight 0 < 1\n
    if weight next:now store\n
;\n
\n
store\n
    if !some_item \"welcome, \nlook around\"\n
;";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let ev = Evaluator::new(&mut env);
    let (vars,_) = ev.last().unwrap();
    
    assert_eq!(vars[0], "welcome, \nlook around".into());
}

#[test]
fn parse_select_nodes() {
    let src = "root\n
    next:select {\"Head to Store?\" store,\n
                \"Leave the town?\" exit-town}\n
\n
    if !some_item [\"Some choices\"
        next:select {\"Head to Store?\" store2,\n
                    \"Leave the town?\" exit-town2}]\n
\n
    next:select {\"Head to town?\" store3 bakery tanner,\n
                5 hike,
                \"Leave the town?\" exit-town3}\n
\n
    emit \"A dustball blows by\"\n
;";
    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (_vars,select1) = ev.next().unwrap();
    let (_vars,select2) = ev.next().unwrap();

    assert_ne!(select1,select2);
    
    let mut map: Map = HashMap::new();
    map.insert("Head to Store?".to_owned(), vec![Var::Sym("store2".to_owned())]);
    map.insert("Leave the town?".to_owned(), vec![Var::Sym("exit-town2".to_owned())]);
    
    assert_eq!(select2, Some(Next::Select(map)));

    let (_,select) = ev.next().unwrap();
    match select.expect("Unable to parse map") {
        Next::Select(map) => {
            println!("Map: {:?}",map);
            assert!(map.contains_key("5"));
            assert_eq!(map.get("5"), Some(&vec![Var::Sym("hike".to_owned())]));
        },
        _ => { panic!("Invalid Next type found") }
    }
}



#[test]
fn parse_next_back_restart() {
    let src = "root\n
    next:call step2
    next:restart\n
;\n
step2
    next:back\n
    emit \"something\"\n
;\n";

    let p = Parser::parse_blocks(src).expect("ERROR: Unable to parse source");
    let mut env = p.into_env();

    
    let mut ev = Evaluator::new(&mut env);
    
    let (_,next) = ev.next().unwrap();
    assert_eq!(next, Some(Next::Call("step2".to_owned())));

    let (_,next) = ev.next().unwrap();
    assert_eq!(next, Some(Next::Back));

    let (_,next) = ev.next().unwrap();
    assert_eq!(next, Some(Next::Restart(None)));
}

#[test]
fn parse_or_logic() {
    let src = "root\n
    has_weight 101 < 100\n
    if !has_weight \"can add stuff\"\n
    or \"too heavy!\"\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    let (vars,_) = ev.next().unwrap();
    
    assert_eq!(vars[0], "can add stuff".into());
}


#[test]
fn validate_inv_logic() {
    let src = "root\n
    if !global.name \"missing name\"\n
    or \"name is `global.name\"\n
\n
    if global.is_false \"is_false\"\n
    when {!global.name @global.name \"new-name\"}\n
    emit global.name\n
;\n
def global\n
    is_false false\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    
    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], "missing name".into());
    
    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], "new-name".into());
}

#[test]
fn obj_dupe_as_nested() {
    let src = "def daggers\n
  damage 1.5\n
;\n

root\n
  @player.dagger new daggers\n
  emit player.dagger.damage\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    
    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], 1.5 .into());
}

#[test]
fn obj_dupe_mut() {
    let src = "def daggers\n
  damage 1.5\n
;\n

root\n
  @player.dagger new daggers\n
  @player.dagger.age 5\n
  emit player.dagger.age\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    
    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], 5. .into());
}

#[test]
fn obj_from_scratch() {
    let src = "root\n
  @player.dagger.age 5\n
  emit player.dagger.age\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();

    let mut ev = Evaluator::new(&mut env);
    
    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], 5. .into());
}

#[test]
fn inline_logic_inv() {
    let src = "root\n
  weight 4 < 5\n
  if !weight 4.\n
\n
  weight2 5 < 4\n
  if !weight2 5.\n
;\n";

    let mut env = Parser::parse_blocks(src).expect("ERROR: Unable to parse source").into_env();
    let mut ev = Evaluator::new(&mut env);

    let (vars,_) = ev.next().unwrap();
    assert_eq!(vars[0], 5. .into());
}