mirror of
https://github.com/Zenithsiz/zbuild.git
synced 2026-02-05 15:31:55 +00:00
Expressions are now always a single string.
Expression operators are now alias and pattern operators. Added pattern operator `NonEmpty`.
This commit is contained in:
parent
f97f238393
commit
e4fe3071a7
208
src/ast.rs
208
src/ast.rs
@ -44,25 +44,9 @@ pub enum Target {
|
||||
|
||||
/// Expression
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Expr {
|
||||
/// Operation
|
||||
Op {
|
||||
/// Operation
|
||||
op: ExprOp,
|
||||
|
||||
/// Expr
|
||||
expr: Box<Self>,
|
||||
},
|
||||
|
||||
/// String
|
||||
String(Vec<ExprCmpt>),
|
||||
}
|
||||
|
||||
/// Expression operator
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ExprOp {
|
||||
/// Dir name
|
||||
DirName,
|
||||
pub struct Expr {
|
||||
/// Components
|
||||
pub cmpts: Vec<ExprCmpt>,
|
||||
}
|
||||
|
||||
/// Expression component
|
||||
@ -72,10 +56,24 @@ pub enum ExprCmpt {
|
||||
String(String),
|
||||
|
||||
/// Pattern
|
||||
Pattern(String),
|
||||
Pattern { name: String, ops: Vec<PatternOp> },
|
||||
|
||||
/// Alias
|
||||
Alias(String),
|
||||
Alias { name: String, ops: Vec<AliasOp> },
|
||||
}
|
||||
|
||||
/// Pattern operator
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PatternOp {
|
||||
/// Non-empty
|
||||
NonEmpty,
|
||||
}
|
||||
|
||||
/// Alias operator
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AliasOp {
|
||||
/// Directory name
|
||||
DirName,
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Expr {
|
||||
@ -83,98 +81,94 @@ impl<'de> serde::Deserialize<'de> for Expr {
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
struct Visitor;
|
||||
// Parse the string
|
||||
// TODO: Allow arrays and concat them?
|
||||
// TODO: Deserialize a `Cow<str>`?
|
||||
let inner = String::deserialize(deserializer)?;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for Visitor {
|
||||
type Value = Expr;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(formatter, "Map with single key for operation or string")
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: serde::de::MapAccess<'de>,
|
||||
{
|
||||
// Get the entry
|
||||
let (op, expr) = map
|
||||
.next_entry::<String, Expr>()?
|
||||
.ok_or_else(|| A::Error::custom("Unexpected empty map"))?;
|
||||
|
||||
// Make sure there isn't another one
|
||||
if let Some((key, _)) = map.next_entry::<String, Expr>()? {
|
||||
return Err(A::Error::custom(format!("Unexpected second map entry: {key:?}")));
|
||||
}
|
||||
|
||||
// Then parse the operation
|
||||
let op = match op.as_str() {
|
||||
"dir_name" => ExprOp::DirName,
|
||||
_ => return Err(A::Error::custom(format!("Unknown expression operation: {op:?}"))),
|
||||
};
|
||||
|
||||
Ok(Expr::Op {
|
||||
op,
|
||||
expr: Box::new(expr),
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, mut s: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
let mut components = vec![];
|
||||
loop {
|
||||
// Try to find the next pattern / alias
|
||||
match s.find(['$', '^']) {
|
||||
// If we found it
|
||||
Some(idx) => {
|
||||
// Add the string until it, if it isn't empty
|
||||
if !s[..idx].is_empty() {
|
||||
components.push(ExprCmpt::String(s[..idx].to_owned()));
|
||||
}
|
||||
|
||||
// Then check which one we got
|
||||
let mut chars = s[idx..].chars();
|
||||
let cmpt_fn = match chars.next() {
|
||||
Some('$') => ExprCmpt::Alias,
|
||||
Some('^') => ExprCmpt::Pattern,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Make sure it's valid
|
||||
match chars.next() {
|
||||
Some('(') => (),
|
||||
Some(ch) => return Err(E::custom(format!("Expected `(` after `$`, found {ch:?}"))),
|
||||
None => return Err(E::custom("Expected `(` after `$`")),
|
||||
};
|
||||
|
||||
// Then read until `)`
|
||||
s = chars.as_str();
|
||||
let alias;
|
||||
(alias, s) = s
|
||||
.split_once(')')
|
||||
.ok_or_else(|| E::custom("Alias `$(` has no closing brace"))?;
|
||||
|
||||
components.push(cmpt_fn(alias.to_owned()));
|
||||
},
|
||||
|
||||
// If we didn't find any, the rest of the expression if a string
|
||||
None => {
|
||||
// Add the rest only if it isn't empty
|
||||
if !s.is_empty() {
|
||||
components.push(ExprCmpt::String(s.to_owned()))
|
||||
}
|
||||
break;
|
||||
},
|
||||
// Then parse all components
|
||||
let mut cmpts = vec![];
|
||||
let mut rest = inner.as_str();
|
||||
loop {
|
||||
// Try to find the next pattern / alias
|
||||
match rest.find(['$', '^']) {
|
||||
// If we found it
|
||||
Some(idx) => {
|
||||
// Add the string until the pattern / alias, if it isn't empty
|
||||
if !rest[..idx].is_empty() {
|
||||
cmpts.push(ExprCmpt::String(rest[..idx].to_owned()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Expr::String(components))
|
||||
// Then check if it was an alias or pattern
|
||||
enum Kind {
|
||||
Alias,
|
||||
Pattern,
|
||||
}
|
||||
let mut chars = rest[idx..].chars();
|
||||
let kind = match chars.next() {
|
||||
Some('$') => Kind::Alias,
|
||||
Some('^') => Kind::Pattern,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Ensure it starts with `(`
|
||||
match chars.next() {
|
||||
Some('(') => (),
|
||||
Some(ch) => return Err(D::Error::custom(format!("Expected `(` after `$`, found {ch:?}"))),
|
||||
None => return Err(D::Error::custom("Expected `(` after `$`")),
|
||||
};
|
||||
|
||||
// Then read until `)`
|
||||
rest = chars.as_str();
|
||||
let inner;
|
||||
(inner, rest) = rest
|
||||
.split_once(')')
|
||||
.ok_or_else(|| D::Error::custom("Alias `$(` has no closing brace"))?;
|
||||
|
||||
// And split all operations
|
||||
let (name, ops) = match inner.split_once("::") {
|
||||
Some((name, ops)) => (name, ops.split("::").collect()),
|
||||
None => (inner, vec![]),
|
||||
};
|
||||
|
||||
|
||||
let cmpt = match kind {
|
||||
Kind::Alias => ExprCmpt::Alias {
|
||||
name: name.to_owned(),
|
||||
ops: ops
|
||||
.into_iter()
|
||||
.map(|op| match op.trim() {
|
||||
"dir_name" => Ok(AliasOp::DirName),
|
||||
op => Err(D::Error::custom(format!("Unknown alias operator {op:?}"))),
|
||||
})
|
||||
.collect::<Result<_, _>>()?,
|
||||
},
|
||||
Kind::Pattern => ExprCmpt::Pattern {
|
||||
name: name.to_owned(),
|
||||
ops: ops
|
||||
.into_iter()
|
||||
.map(|op| match op.trim() {
|
||||
"non_empty" => Ok(PatternOp::NonEmpty),
|
||||
op => Err(D::Error::custom(format!("Unknown pattern operator {op:?}"))),
|
||||
})
|
||||
.collect::<Result<_, _>>()?,
|
||||
},
|
||||
};
|
||||
cmpts.push(cmpt);
|
||||
},
|
||||
|
||||
// If we didn't find any, the rest of the expression if a string
|
||||
None => {
|
||||
// Add the rest only if it isn't empty
|
||||
if !rest.is_empty() {
|
||||
cmpts.push(ExprCmpt::String(rest.to_owned()))
|
||||
}
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Not use `_any`?
|
||||
deserializer.deserialize_any(Visitor)
|
||||
Ok(Expr { cmpts })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -229,7 +229,6 @@ impl Builder {
|
||||
.max_by_key(|res| res.build_time);
|
||||
|
||||
let output_last_build_time = self::rule_last_build_time(&rule).ok().flatten();
|
||||
tracing::trace!(?target, ?output_last_build_time, ?deps_res, "Check rebuild");
|
||||
let needs_rebuilt = match (deps_res, output_last_build_time) {
|
||||
// If not built, rebuild
|
||||
(_, None) => true,
|
||||
|
||||
@ -2,43 +2,63 @@
|
||||
|
||||
// Imports
|
||||
use {
|
||||
crate::rules::{Alias, Expr, ExprCmpt, ExprOp, Pattern},
|
||||
crate::rules::{AliasOp, Expr, ExprCmpt},
|
||||
anyhow::Context,
|
||||
std::{borrow::Cow, collections::HashMap, path::PathBuf},
|
||||
};
|
||||
|
||||
/// Expands an expression to it's components
|
||||
pub fn expand_expr(expr: &Expr, visitor: &mut impl Visitor) -> Result<Vec<ExprCmpt>, anyhow::Error> {
|
||||
let cmpts = match expr {
|
||||
Expr::Op { op, expr } => {
|
||||
let s = self::expand_expr_string(expr, visitor)
|
||||
.with_context(|| format!("Unable to expand expression to string for operator {expr:?}"))?;
|
||||
let s = self::expand_op(op, s)?;
|
||||
|
||||
vec![ExprCmpt::String(s)]
|
||||
},
|
||||
|
||||
// If we got a string, simply expand it
|
||||
Expr::String(cmpts) => cmpts.iter().try_fold::<_, _, Result<_, _>>(vec![], |mut cmpts, cmpt| {
|
||||
// Go through all components
|
||||
expr.cmpts
|
||||
.iter()
|
||||
.try_fold::<_, _, Result<_, _>>(vec![], |mut cmpts, cmpt| {
|
||||
match cmpt {
|
||||
// If it's a string, we keep it
|
||||
ExprCmpt::String(_) => cmpts.push(cmpt.clone()),
|
||||
ExprCmpt::Pattern(pat) => match visitor.visit_pat(pat) {
|
||||
FlowControl::ExpandTo(s) => cmpts.push(ExprCmpt::String(s)),
|
||||
|
||||
// If it's a pattern, we visit it
|
||||
// Note: We don't care about the operations on patterns, those are for matching
|
||||
ExprCmpt::Pattern(pat) => match visitor.visit_pat(&pat.name) {
|
||||
// If expanded, just replace it with a string
|
||||
FlowControl::ExpandTo(value) => cmpts.push(ExprCmpt::String(value)),
|
||||
|
||||
// Else keep on Keep and error on Error
|
||||
FlowControl::Keep => cmpts.push(cmpt.clone()),
|
||||
FlowControl::Error => anyhow::bail!("Unknown pattern {pat:?}"),
|
||||
},
|
||||
ExprCmpt::Alias(alias) => match visitor.visit_alias(alias) {
|
||||
FlowControl::ExpandTo(expr) => cmpts.extend(self::expand_expr(&expr, visitor)?),
|
||||
|
||||
// If it's an alias, we visit and then expand it
|
||||
ExprCmpt::Alias(alias) => match visitor.visit_alias(&alias.name) {
|
||||
// If expanded, check if we need to apply any operations
|
||||
FlowControl::ExpandTo(expr) => match alias.ops.is_empty() {
|
||||
// If not, just recursively expand it
|
||||
true => cmpts.extend(self::expand_expr(&expr, visitor)?),
|
||||
|
||||
// Else expand it to a string, then apply all operations
|
||||
// TODO: Apply operations on the expression components without expanding to a string?
|
||||
false => {
|
||||
// Expand
|
||||
let value = self::expand_expr_string(&expr, visitor)?;
|
||||
|
||||
// Then apply all
|
||||
let value = alias.ops.iter().try_fold(value, |value, &op| {
|
||||
self::expand_alias_op(op, value)
|
||||
.with_context(|| format!("Unable to apply alias operator {op:?}"))
|
||||
})?;
|
||||
|
||||
cmpts.push(ExprCmpt::String(value))
|
||||
},
|
||||
},
|
||||
|
||||
// Else keep on Keep and error on Error
|
||||
FlowControl::Keep => cmpts.push(cmpt.clone()),
|
||||
FlowControl::Error => anyhow::bail!("Unknown alias {alias:?}"),
|
||||
},
|
||||
};
|
||||
|
||||
Ok(cmpts)
|
||||
})?,
|
||||
};
|
||||
|
||||
Ok(cmpts)
|
||||
})
|
||||
}
|
||||
|
||||
/// Expands an expression into a string
|
||||
@ -46,49 +66,55 @@ pub fn expand_expr(expr: &Expr, visitor: &mut impl Visitor) -> Result<Vec<ExprCm
|
||||
/// Panics if `visitor` returns `Keep`.
|
||||
// TODO: Merge neighboring strings in output.
|
||||
pub fn expand_expr_string(expr: &Expr, visitor: &mut impl Visitor) -> Result<String, anyhow::Error> {
|
||||
let s = match expr {
|
||||
Expr::Op { op, expr } => {
|
||||
let expr = self::expand_expr_string(expr, visitor)?;
|
||||
self::expand_op(op, expr)?
|
||||
},
|
||||
expr.cmpts
|
||||
.iter()
|
||||
.try_fold::<_, _, Result<_, _>>(String::new(), |mut string, cmpt| {
|
||||
let s = match cmpt {
|
||||
ExprCmpt::String(s) => Cow::Borrowed(s),
|
||||
ExprCmpt::Pattern(pat) => match visitor.visit_pat(&pat.name) {
|
||||
FlowControl::ExpandTo(s) => Cow::Owned(s),
|
||||
FlowControl::Keep => panic!("Cannot keep unknown pattern when expanding to string"),
|
||||
FlowControl::Error => anyhow::bail!("Unknown pattern {pat:?}"),
|
||||
},
|
||||
ExprCmpt::Alias(alias) => {
|
||||
match visitor.visit_alias(&alias.name) {
|
||||
FlowControl::ExpandTo(expr) => match alias.ops.is_empty() {
|
||||
// If not, just recursively expand it
|
||||
true => self::expand_expr_string(&expr, visitor).map(Cow::Owned)?,
|
||||
|
||||
// If we got a string, simply expand it
|
||||
Expr::String(cmpts) => cmpts
|
||||
.iter()
|
||||
.try_fold::<_, _, Result<_, _>>(String::new(), |mut string, cmpt| {
|
||||
let s = match cmpt {
|
||||
ExprCmpt::String(s) => Cow::Borrowed(s),
|
||||
ExprCmpt::Pattern(pat) => match visitor.visit_pat(pat) {
|
||||
FlowControl::ExpandTo(s) => Cow::Owned(s),
|
||||
// Else expand it to a string, then apply all operations
|
||||
// TODO: Apply operations on the expression components without expanding to a string?
|
||||
false => {
|
||||
// Expand
|
||||
let value = self::expand_expr_string(&expr, visitor)?;
|
||||
|
||||
// Then apply all
|
||||
let value = alias.ops.iter().try_fold(value, |value, &op| {
|
||||
self::expand_alias_op(op, value)
|
||||
.with_context(|| format!("Unable to apply alias operator {op:?}"))
|
||||
})?;
|
||||
|
||||
Cow::Owned(value)
|
||||
},
|
||||
},
|
||||
FlowControl::Keep => panic!("Cannot keep unknown pattern when expanding to string"),
|
||||
FlowControl::Error => anyhow::bail!("Unknown pattern {pat:?}"),
|
||||
},
|
||||
ExprCmpt::Alias(alias) => {
|
||||
let expr = match visitor.visit_alias(alias) {
|
||||
FlowControl::ExpandTo(expr) => expr,
|
||||
FlowControl::Keep => panic!("Cannot keep unknown pattern when expanding to string"),
|
||||
FlowControl::Error => anyhow::bail!("Unknown alias {alias:?}"),
|
||||
};
|
||||
let s = self::expand_expr_string(&expr, visitor)?;
|
||||
Cow::Owned(s)
|
||||
},
|
||||
};
|
||||
FlowControl::Error => anyhow::bail!("Unknown alias {alias:?}"),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
string.push_str(&s);
|
||||
Ok(string)
|
||||
})?,
|
||||
};
|
||||
|
||||
Ok(s)
|
||||
string.push_str(&s);
|
||||
Ok(string)
|
||||
})
|
||||
}
|
||||
|
||||
/// Expands an operation on an expression
|
||||
fn expand_op(op: &ExprOp, expr: String) -> Result<String, anyhow::Error> {
|
||||
let expr = match op {
|
||||
/// Expands an alias operation on the value of that alias
|
||||
fn expand_alias_op(op: AliasOp, value: String) -> Result<String, anyhow::Error> {
|
||||
let value = match op {
|
||||
// TODO: Not add `/` here.
|
||||
ExprOp::DirName => {
|
||||
AliasOp::DirName => {
|
||||
// Get the path and try to pop the last segment
|
||||
let mut path = PathBuf::from(expr);
|
||||
let mut path = PathBuf::from(value);
|
||||
anyhow::ensure!(path.pop(), "Path {path:?} had no directory name");
|
||||
|
||||
// Then convert it back to a string
|
||||
@ -103,7 +129,7 @@ fn expand_op(op: &ExprOp, expr: String) -> Result<String, anyhow::Error> {
|
||||
},
|
||||
};
|
||||
|
||||
Ok(expr)
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Flow control for [`expand_expr_string`]
|
||||
@ -121,10 +147,10 @@ pub enum FlowControl<T> {
|
||||
/// Visitor for [`expand_expr_string`]
|
||||
pub trait Visitor {
|
||||
/// Visits an alias
|
||||
fn visit_alias(&mut self, alias: &Alias) -> FlowControl<Expr>;
|
||||
fn visit_alias(&mut self, alias_name: &str) -> FlowControl<Expr>;
|
||||
|
||||
/// Visits a pattern
|
||||
fn visit_pat(&mut self, pat: &Pattern) -> FlowControl<String>;
|
||||
fn visit_pat(&mut self, pat_name: &str) -> FlowControl<String>;
|
||||
}
|
||||
|
||||
/// Visitor for global aliases.
|
||||
@ -145,14 +171,14 @@ impl<'global> GlobalVisitor<'global> {
|
||||
}
|
||||
|
||||
impl<'global> Visitor for GlobalVisitor<'global> {
|
||||
fn visit_alias(&mut self, alias: &Alias) -> FlowControl<Expr> {
|
||||
match self.aliases.get(&alias.name).cloned() {
|
||||
fn visit_alias(&mut self, alias_name: &str) -> FlowControl<Expr> {
|
||||
match self.aliases.get(alias_name).cloned() {
|
||||
Some(expr) => FlowControl::ExpandTo(expr),
|
||||
None => FlowControl::Error,
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, _pat: &Pattern) -> FlowControl<String> {
|
||||
fn visit_pat(&mut self, _pat: &str) -> FlowControl<String> {
|
||||
FlowControl::Error
|
||||
}
|
||||
}
|
||||
@ -181,14 +207,14 @@ impl<'global, 'rule> RuleOutputVisitor<'global, 'rule> {
|
||||
}
|
||||
|
||||
impl<'global, 'rule> Visitor for RuleOutputVisitor<'global, 'rule> {
|
||||
fn visit_alias(&mut self, alias: &Alias) -> FlowControl<Expr> {
|
||||
match self.rule_aliases.get(&alias.name).cloned() {
|
||||
fn visit_alias(&mut self, alias_name: &str) -> FlowControl<Expr> {
|
||||
match self.rule_aliases.get(alias_name).cloned() {
|
||||
Some(expr) => FlowControl::ExpandTo(expr),
|
||||
None => self.global_visitor.visit_alias(alias),
|
||||
None => self.global_visitor.visit_alias(alias_name),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, _pat: &Pattern) -> FlowControl<String> {
|
||||
fn visit_pat(&mut self, _pat: &str) -> FlowControl<String> {
|
||||
FlowControl::Keep
|
||||
}
|
||||
}
|
||||
@ -217,12 +243,12 @@ impl<'global, 'rule, 'pats> RuleVisitor<'global, 'rule, 'pats> {
|
||||
}
|
||||
|
||||
impl<'global, 'rule, 'pats> Visitor for RuleVisitor<'global, 'rule, 'pats> {
|
||||
fn visit_alias(&mut self, alias: &Alias) -> FlowControl<Expr> {
|
||||
self.rule_output_visitor.visit_alias(alias)
|
||||
fn visit_alias(&mut self, alias_name: &str) -> FlowControl<Expr> {
|
||||
self.rule_output_visitor.visit_alias(alias_name)
|
||||
}
|
||||
|
||||
fn visit_pat(&mut self, pat: &Pattern) -> FlowControl<String> {
|
||||
match self.pats.get(&pat.name).cloned() {
|
||||
fn visit_pat(&mut self, pat_name: &str) -> FlowControl<String> {
|
||||
match self.pats.get(pat_name).cloned() {
|
||||
Some(value) => FlowControl::ExpandTo(value),
|
||||
None => FlowControl::Error,
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
//! Expression matching
|
||||
|
||||
// Imports
|
||||
use {crate::rules::ExprCmpt, std::collections::HashMap};
|
||||
use {
|
||||
crate::rules::{ExprCmpt, PatternOp},
|
||||
std::collections::HashMap,
|
||||
};
|
||||
|
||||
/// Returns if `value` matches all `cmpts` and returns all patterns resolved
|
||||
///
|
||||
@ -30,8 +33,26 @@ pub fn match_expr(mut cmpts: &[ExprCmpt], mut value: &str) -> Result<Option<Hash
|
||||
None => return Ok(None),
|
||||
},
|
||||
|
||||
// If we're a single pattern, we fully match anything on the right
|
||||
// If we're a single pattern, check for operators
|
||||
[ExprCmpt::Pattern(pat)] => {
|
||||
let mut ops = pat.ops.as_slice();
|
||||
loop {
|
||||
match ops {
|
||||
// If we're empty, match everything
|
||||
[] => break,
|
||||
|
||||
// On non-empty check if the rest of the value is empty
|
||||
[PatternOp::NonEmpty, rest @ ..] => match value.is_empty() {
|
||||
// If so, we don't match anything
|
||||
true => return Ok(None),
|
||||
|
||||
// Else continue checking the rest of the operators
|
||||
false => ops = rest,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, match everything
|
||||
patterns.insert(pat.name.clone(), value.to_owned());
|
||||
cmpts = &[];
|
||||
value = "";
|
||||
|
||||
@ -10,10 +10,10 @@ mod target;
|
||||
|
||||
// Exports
|
||||
pub use {
|
||||
alias::Alias,
|
||||
expr::{Expr, ExprCmpt, ExprOp},
|
||||
alias::{Alias, AliasOp},
|
||||
expr::{Expr, ExprCmpt},
|
||||
item::Item,
|
||||
pattern::Pattern,
|
||||
pattern::{Pattern, PatternOp},
|
||||
rule::{Command, Rule},
|
||||
target::Target,
|
||||
};
|
||||
|
||||
@ -5,4 +5,14 @@
|
||||
pub struct Alias {
|
||||
/// Alias name
|
||||
pub name: String,
|
||||
|
||||
/// Operators
|
||||
pub ops: Vec<AliasOp>,
|
||||
}
|
||||
|
||||
/// Alias operator
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum AliasOp {
|
||||
/// Directory name
|
||||
DirName,
|
||||
}
|
||||
|
||||
@ -2,31 +2,18 @@
|
||||
|
||||
// Imports
|
||||
use {
|
||||
super::{alias::Alias, pattern::Pattern},
|
||||
super::{
|
||||
alias::{Alias, AliasOp},
|
||||
pattern::{Pattern, PatternOp},
|
||||
},
|
||||
crate::ast,
|
||||
};
|
||||
|
||||
/// Expression
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Expr {
|
||||
/// Operation
|
||||
Op {
|
||||
/// Operation
|
||||
op: ExprOp,
|
||||
|
||||
/// Expression
|
||||
expr: Box<Self>,
|
||||
},
|
||||
|
||||
/// String
|
||||
String(Vec<ExprCmpt>),
|
||||
}
|
||||
|
||||
/// Expression operator
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ExprOp {
|
||||
/// Dir name
|
||||
DirName,
|
||||
pub struct Expr {
|
||||
/// Components
|
||||
pub cmpts: Vec<ExprCmpt>,
|
||||
}
|
||||
|
||||
/// Expression component
|
||||
@ -45,28 +32,39 @@ pub enum ExprCmpt {
|
||||
impl Expr {
|
||||
/// Creates a new expression from it's ast
|
||||
pub fn new(expr: ast::Expr) -> Self {
|
||||
match expr {
|
||||
ast::Expr::Op { op, expr } => Self::Op {
|
||||
op: match op {
|
||||
ast::ExprOp::DirName => ExprOp::DirName,
|
||||
},
|
||||
expr: Box::new(Self::new(*expr)),
|
||||
},
|
||||
ast::Expr::String(cmpts) => Self::String(
|
||||
cmpts
|
||||
.into_iter()
|
||||
.map(|cmpt| match cmpt {
|
||||
ast::ExprCmpt::String(s) => ExprCmpt::String(s),
|
||||
ast::ExprCmpt::Pattern(name) => ExprCmpt::Pattern(Pattern { name }),
|
||||
ast::ExprCmpt::Alias(name) => ExprCmpt::Alias(Alias { name }),
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
let cmpts = expr
|
||||
.cmpts
|
||||
.into_iter()
|
||||
.map(|cmpt| match cmpt {
|
||||
ast::ExprCmpt::String(s) => ExprCmpt::String(s),
|
||||
ast::ExprCmpt::Pattern { name, ops } => ExprCmpt::Pattern(Pattern {
|
||||
name,
|
||||
ops: ops
|
||||
.into_iter()
|
||||
.map(|op| match op {
|
||||
ast::PatternOp::NonEmpty => PatternOp::NonEmpty,
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
ast::ExprCmpt::Alias { name, ops } => ExprCmpt::Alias(Alias {
|
||||
name,
|
||||
ops: ops
|
||||
.into_iter()
|
||||
.map(|op| match op {
|
||||
ast::AliasOp::DirName => AliasOp::DirName,
|
||||
})
|
||||
.collect(),
|
||||
}),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self { cmpts }
|
||||
}
|
||||
|
||||
/// Returns an expression that's just a string
|
||||
pub fn string(value: String) -> Self {
|
||||
Self::String(vec![ExprCmpt::String(value)])
|
||||
Self {
|
||||
cmpts: vec![ExprCmpt::String(value)],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,4 +5,14 @@
|
||||
pub struct Pattern {
|
||||
/// Pattern name
|
||||
pub name: String,
|
||||
|
||||
/// Operators
|
||||
pub ops: Vec<PatternOp>,
|
||||
}
|
||||
|
||||
/// Pattern operator
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum PatternOp {
|
||||
/// Non-empty
|
||||
NonEmpty,
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user