|
|
@ -5,7 +5,7 @@ use crate::{
|
|
|
|
parse,
|
|
|
|
parse,
|
|
|
|
run::{Call, Context, Eval, Value},
|
|
|
|
run::{Call, Context, Eval, Value},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
use std::{collections::HashSet, process};
|
|
|
|
use std::{collections::HashSet, env, ffi::OsString, process};
|
|
|
|
|
|
|
|
|
|
|
|
/// The differnt types of nodes that may appear in our AST
|
|
|
|
/// The differnt types of nodes that may appear in our AST
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[derive(Debug)]
|
|
|
@ -170,7 +170,9 @@ impl TreeBuilder {
|
|
|
|
Element::Command(cmd)
|
|
|
|
Element::Command(cmd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parse::Value::Terminal(Token::Word(word)) => {
|
|
|
|
parse::Value::Terminal(Token::Word(word)) => {
|
|
|
|
Element::Literal(Value::Text(word.to_string()))
|
|
|
|
let text = word.to_string();
|
|
|
|
|
|
|
|
let text = expand(&text)?;
|
|
|
|
|
|
|
|
Element::Literal(Value::Text(text))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parse::Value::Terminal(Token::Glob(_)) => {
|
|
|
|
parse::Value::Terminal(Token::Glob(_)) => {
|
|
|
|
todo!()
|
|
|
|
todo!()
|
|
|
@ -192,7 +194,49 @@ pub fn parse(source: &str) -> Result<Element, ParseError> {
|
|
|
|
let parser = parse::Parser::new(tokens);
|
|
|
|
let parser = parse::Parser::new(tokens);
|
|
|
|
let mut parse_tree = parser.parse()?;
|
|
|
|
let mut parse_tree = parser.parse()?;
|
|
|
|
let mut builder = TreeBuilder::new();
|
|
|
|
let mut builder = TreeBuilder::new();
|
|
|
|
builder.parse(&mut parse_tree)
|
|
|
|
let root = builder.parse(&mut parse_tree)?;
|
|
|
|
|
|
|
|
Ok(root)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
|
|
|
|
fn home_dir() -> Result<OsString, ExpandError> {
|
|
|
|
|
|
|
|
env::var_os("HOMEPATH").ok_or(ExpandError::EmptyEnvironmentVar(String::from("HOMEPATH")))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
|
|
|
|
pub enum ExpandError {
|
|
|
|
|
|
|
|
#[error("environment var is not set: {0}")]
|
|
|
|
|
|
|
|
EmptyEnvironmentVar(String),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[error("home dir is not utf-8")]
|
|
|
|
|
|
|
|
NotUnicode,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn expand(text: &str) -> Result<String, ExpandError> {
|
|
|
|
|
|
|
|
let chars = text.chars();
|
|
|
|
|
|
|
|
let mut escape = false;
|
|
|
|
|
|
|
|
let mut s = String::new();
|
|
|
|
|
|
|
|
for c in chars {
|
|
|
|
|
|
|
|
if !escape && c == '\\' {
|
|
|
|
|
|
|
|
escape = true;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if escape {
|
|
|
|
|
|
|
|
s.push(c);
|
|
|
|
|
|
|
|
escape = false;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if c == '~' {
|
|
|
|
|
|
|
|
let p = home_dir()?;
|
|
|
|
|
|
|
|
s.push_str(p.to_str().ok_or(ExpandError::NotUnicode)?);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s.push(c);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
#[cfg(test)]
|
|
|
|