ru_shell 0.1.0

A Shell built around a semantic grammar
use crate::args::{Arg, Args};
use crate::channel::Channel;
use crate::exec::{Connection, Exec};
use crate::expr::Expr;
use crate::statement::Statement as Stt;
use bogobble::*;

parser! {(Path->String)
    string((maybe("~"),plus(or_ig!("\\ ",("/._",Alpha,NumDigit).iplus()))))
}

parser! {(End->())
    ws_(or_ig!("\n;".one(),EOI))
}

parser! {(ExChannel ->Channel)
    or!(
        "^^".asv(Channel::Both),
        "^".asv(Channel::StdErr),
        "".asv(Channel::StdOut),

    )
}

parser! {(Lines -> Vec<Stt>)
    first(star(ws_(FullStatement)),EOI)
}

parser! {(FullStatement->Stt)
    first(Statement,bogobble::partial::p_plus(End))
}

parser! {(Statement->Stt)
    or!(
        (keyword("let"),plus(ws_(common::Ident)),ws_("="),ArgsS).map(|(_,ids,_,args)|Stt::Let(ids,args)),
        (keyword("export"),plus(ws_(common::Ident)),ws_("="),ArgsS).map(|(_,ids,_,args)|Stt::Export(ids,args)),
        (keyword("cd"),ws_(ArgP)).map(|(_,a)|Stt::Cd(a)),
        (keyword("for"),plus_until(ws_(common::Ident),ws_(keyword("in"))),ArgsP,Block).map(|(_,(vars,_),args,block)|Stt::For{vars,args,block}),
        (fail_on(keyword(or!("for","export","cd","let"))),
        ExprRight).map(|(_,e)|Stt::Expr(e)),
    )
}

parser! {(Block->Vec<Stt>)
    (wn_("{"),star_until(wn_(FullStatement),keyword("}"))).map(|(_,(a,_))|a)
}

parser! {(ExprLeft ->Expr)
    (PExec,ws_(maybe((ExChannel,">",exists(">"),ws_(ArgP))))).map(|(exec,wop)|{
        match wop {
            Some((chan,_,append,filename))=>Expr::Write{exec,chan,append,filename},
            None=>Expr::Exec(exec),
        }})

}
parser! {(ExprRight -> Expr)
    (ExprLeft,maybe((ws_(or("&&","||")),wn_(ExprRight)))).map(|(lt,op)|{
        match op {
            Some(("&&",rt))=>Expr::And(Box::new(lt),Box::new(rt)),
            Some(("||",rt))=>Expr::Or(Box::new(lt),Box::new(rt)),
            _=>lt,
        }

    })
}

parser! {(ExTarget->Exec)
     ("|",ws_(PExec)).map(|(_,e)|e),
}

parser! {(PConnection->Connection)
    (ExChannel,ExTarget).map(|(chan,target)|Connection{chan,target:Box::new(target)})
}

parser! {(PExec->Exec)
    (Path , ArgsS,maybe(ws_(PConnection))).map(|(command,args,conn)|Exec{command,args,conn})
}

parser! {(ArgsP->Args)
    plus(ws_(ArgP)).map(|v|Args(v))
}

parser! {(ArgsS -> Args)
    star(ws_(ArgP)).map(|v| Args(v))
}

parser! { (QuotedLitString->String)
    strings_plus(or!(
            string(not("${}()[]\\\"").plus()),
            "\\n".map(|_|"\n".to_string()),
            "\\t".map(|_|"\t".to_string()),
            "\\e".map(|_|"\u{1b}[".to_string()),
             ("\\",Any.one()).map(|(_,c)| {let mut s=String::new(); s.push(c);s}),
    ))
}

parser! { (LitString->String)
    strings_plus(or!(
            string(not("&$|^{}()[]\\\" \n\t<>;").plus()),
            "\\n".map(|_|"\n".to_string()),
            "\\t".map(|_|"\t".to_string()),
             ("\\",Any.one()).map(|(_,c)| {let mut s=String::new(); s.push(c);s}),
    ))
}

parser! {(StringPart->Arg)
    or!(
        ("$[",ws__(PExec),"]").map(|(_,e,_)|Arg::ArrCommand(e)),
        ("$(",ws__(PExec),")").map(|(_,e,_)|Arg::Command(e)),
        ("${",ws__(string((Alpha,NumDigit,"_").plus())),"}").map(|(_,s,_)|Arg::Var(s)),
        ("$",string((Alpha,NumDigit,"_").plus())).map(|(_,s)|Arg::Var(s)),
        LitString.map(|s|Arg::StringLit(s)),
    )
}

parser! {(QuotedStringPart->Arg)
    or!(
        ("$[",ws__(PExec),"]").map(|(_,e,_)|Arg::ArrCommand(e)),
        ("$(",ws__(PExec),")").map(|(_,e,_)|Arg::Command(e)),
        ("${",ws__(string((Alpha,NumDigit,"_").plus())),"}").map(|(_,s,_)|Arg::Var(s)),
        ("$",string((Alpha,NumDigit,"_").plus())).map(|(_,s)|Arg::Var(s)),
        QuotedLitString.map(|s|Arg::StringLit(s)),
    )
}

parser! {(QuotedString->Arg)
    star(QuotedStringPart).map(|v| match v.len(){
            0=> Arg::StringLit(String::new()),
            1 => v[0].clone(),
            _=> Arg::StringExpr(v),
        }),

}

parser! { (ArgP->Arg)
    or!(
        ("~",keyword(maybe(LitString))).map(|(_,s)|Arg::HomePath(s.unwrap_or(String::new()))),
        ("~",plus(StringPart)).map(|(_,v)| Arg::HomeExpr(v)),
        r_hash.map(|s|Arg::RawString(s) ) ,
        plus(StringPart).map(|v| match v.len(){
            1 => v[0].clone(),
            _=> Arg::StringExpr(v),
        }),
        ("\"",QuotedString,"\"").map(|(_,s,_)|s),
        )
}

/// Raw strings eg: r###" Any \ "##  wierd \ string "###
pub fn r_hash<'a>(it: &PIter<'a>) -> ParseRes<'a, String> {
    let (it, (_, v, _), _) = ("r", "#".star(), "\"").parse(it)?;
    Any.until(("\"", "#".exact(v.len())))
        .map(|(s, _)| s.to_string())
        .parse(&it)
}