Skip to content

Commit

Permalink
AttrValue / Update
Browse files Browse the repository at this point in the history
  • Loading branch information
druskus20 authored and elkowar committed Apr 26, 2022
1 parent 1daeb0c commit 8877b8f
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 16 deletions.
37 changes: 26 additions & 11 deletions crates/eww/src/widgets/def_widget_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ macro_rules! def_widget {
// If an attribute is explicitly marked as optional (? appended to type)
// the attribute will still show up here, as a `None` value. Otherwise, all values in this map
// will be `Some`.
let attr_map: Result<HashMap<eww_shared_util::AttrName, Option<simplexpr::SimplExpr>>> = try {
let attr_map: Result<HashMap<eww_shared_util::AttrName, Option<yuck::config::attr_value::AttrValue>>> = try {
::maplit::hashmap! {
$(
eww_shared_util::AttrName(::std::stringify!($attr_name).to_owned()) =>
Expand All @@ -32,7 +32,7 @@ macro_rules! def_widget {
// Get all the variables that are referred to in any of the attributes expressions
let required_vars: Vec<eww_shared_util::VarName> = attr_map
.values()
.flat_map(|expr| expr.as_ref().map(|x| x.collect_var_refs()).unwrap_or_default())
.flat_map(|expr| expr.as_ref().and_then(|x| x.try_into_simplexpr()).map(|x| x.collect_var_refs()).unwrap_or_default())
.collect();

$args.scope_graph.register_listener(
Expand All @@ -53,12 +53,13 @@ macro_rules! def_widget {
let $attr_name = attr_map.get(::std::stringify!($attr_name))
.context("Missing attribute, this should never happen")?;

// if the value is Some, evaluate and typecast it as expected
let $attr_name = if let Some(x) = $attr_name {
Some(x.eval(&values)?.$typecast_func()?)
} else {
None
};


// If the value is some, evaluate and typecast it.
// This now uses a new macro, to match on the type cast function:
// if we're casting into an action, we wanna do a different thing than if we where casting into an expr
let $attr_name = def_widget!(@value_depending_on_type values, $attr_name : $typecast_func $(? $(@ $optional @)?)? $(= $default)?);

// If the attribute is optional, keep it as Option<T>, otherwise unwrap
// because we _know_ the value in the attr_map is Some if the attribute is not optional.
def_widget!(@unwrap_if_required $attr_name $(? $($optional)?)?);
Expand All @@ -76,23 +77,37 @@ macro_rules! def_widget {
})+
};

(@value_depending_on_type $values:expr, $attr_name:ident : as_action $(? $(@ $optional:tt @)?)? $(= $default:expr)?) => {
match $attr_name {
Some(yuck::config::attr_value::AttrValue::Action(action)) => Some(action.eval_exprs(&$values)?),
_ => None,
}
};

(@value_depending_on_type $values:expr, $attr_name:ident : $typecast_func:ident $(? $(@ $optional:tt @)?)? $(= $default:expr)?) => {
match $attr_name {
Some(yuck::config::attr_value::AttrValue::SimplExpr(expr)) => Some(expr.eval(&$values)?.$typecast_func()?),
_ => None,
}
};

(@unwrap_if_required $value:ident ?) => { };
(@unwrap_if_required $value:ident) => {
let $value = $value.unwrap();
};

// The attribute is explicitly marked as optional - the value should be provided to the prop function body as Option<T>
(@get_value $args:ident, $name:expr, ?) => {
$args.widget_use.attrs.ast_optional::<simplexpr::SimplExpr>($name)?.clone()
$args.widget_use.attrs.ast_optional::<yuck::config::action::AttrValue>($name)?.clone()
};

// The attribute has a default value
(@get_value $args:ident, $name:expr, = $default:expr) => {
Some($args.widget_use.attrs.ast_optional::<simplexpr::SimplExpr>($name)?.clone().unwrap_or_else(|| simplexpr::SimplExpr::synth_literal($default)))
Some($args.widget_use.attrs.ast_optional::<yuck::config::action::AttrValue>($name)?.clone().unwrap_or_else(|| simplexpr::SimplExpr::synth_literal($default)))
};

// The attribute is required - the prop will only be ran if this attribute is actually provided.
(@get_value $args:ident, $name:expr,) => {
Some($args.widget_use.attrs.ast_required::<simplexpr::SimplExpr>($name)?.clone())
Some($args.widget_use.attrs.ast_required::<yuck::config::action::AttrValue>($name)?.clone())
}
}
22 changes: 22 additions & 0 deletions crates/yuck/src/config/action.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use eww_shared_util::VarName;
use simplexpr::SimplExpr;

pub static ACTION_NAMES: &[&str] = &["update"];

// TODO: Maybe separate that into another file
#[derive(Debug, Clone)]
pub enum AttrValue {
Action(Action),
SimplExpr(SimplExpr),
}

#[derive(Debug, Clone)]
pub enum Action {
Update(Update),
}

#[derive(Debug, Clone)]
pub struct Update {
pub varname: VarName,
pub value: SimplExpr,
}
1 change: 1 addition & 0 deletions crates/yuck/src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod action;
pub mod attributes;
pub mod backend_window_options;
pub mod config;
Expand Down
3 changes: 3 additions & 0 deletions crates/yuck/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub enum AstError {
NotAValue(Span, AstType),
#[error("Expected element {1}, but read {2}")]
MismatchedElementName(Span, String, String),
#[error("Unknown action {1}")]
UnknownAction(Span, String),

#[error("Keyword `{1}` is missing a value")]
DanglingKeyword(Span, String),
Expand Down Expand Up @@ -116,6 +118,7 @@ impl Spanned for AstError {
AstError::NoMoreElementsExpected(span) => *span,
AstError::SimplExpr(err) => err.span(),
AstError::FormFormatError(err) => err.span(),
AstError::UnknownAction(span, ..) => *span,
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions crates/yuck/src/format_diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ impl ToDiagnostic for AstError {
label = span => "Expected some value here",
note = format!("Got: {}", actual),
},
AstError::UnknownAction(span, actual) => gen_diagnostic! {
msg = format!("Unknown action `{}`", actual),
label = span,
note = format!("Must be one of: {}", crate::config::action::ACTION_NAMES.iter().join(", ")),
},

AstError::ParseError { file_id, source } => lalrpop_error_to_diagnostic(source, *file_id),
AstError::MismatchedElementName(span, expected, got) => gen_diagnostic! {
Expand Down
6 changes: 3 additions & 3 deletions crates/yuck/src/parser/ast_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ macro_rules! return_or_put_back {

impl<I: Iterator<Item = Ast>> AstIterator<I> {
return_or_put_back! {
fn expect_symbol -> AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)
fn expect_list -> AstType::List, (Span, Vec<Ast>) = Ast::List(span, x) => (span, x)
fn expect_array -> AstType::Array, (Span, Vec<Ast>) = Ast::Array(span, x) => (span, x)
fn expect_symbol -> AstType::Symbol, (Span, String) = Ast::Symbol(span, x) => (span, x)
fn expect_list -> AstType::List, (Span, Vec<Ast>) = Ast::List(span, x) => (span, x)
fn expect_array -> AstType::Array, (Span, Vec<Ast>) = Ast::Array(span, x) => (span, x)
}

pub fn expect_literal(&mut self) -> AstResult<(Span, DynVal)> {
Expand Down
28 changes: 27 additions & 1 deletion crates/yuck/src/parser/from_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ use super::{
ast::{Ast, AstType},
ast_iterator::AstIterator,
};
use crate::{error::*, parser};
use crate::{
config::action::{AttrValue, Update},
error::*,
parser,
};
use eww_shared_util::{AttrName, Span, VarName};
use itertools::Itertools;
use simplexpr::{ast::SimplExpr, dynval::DynVal};
Expand Down Expand Up @@ -56,3 +60,25 @@ impl FromAst for SimplExpr {
}
}
}

use crate::config::action::Action;
impl FromAst for Action {
fn from_ast(e: Ast) -> AstResult<Self> {
let mut iter = e.try_ast_iter()?;
let (span, action) = iter.expect_symbol()?;
match action.as_str() {
"update" => {
let (varname_span, varname) = iter.expect_symbol()?;
let (value_span, value) = iter.expect_simplexpr()?;
iter.expect_done()?;
Ok(Action::Update(Update { varname: VarName(varname), value }))
}
_ => Err(AstError::UnknownAction(span, action)),
}
}
}
impl FromAst for AttrValue {
fn from_ast(e: Ast) -> AstResult<Self> {
todo!()
}
}
2 changes: 1 addition & 1 deletion crates/yuck/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub fn parse_toplevel(file_id: usize, s: String) -> AstResult<(Span, Vec<Ast>)>
parser.parse(file_id, lexer).map_err(|e| AstError::from_parse_error(file_id, e))
}

/// get a single ast node from a list of asts, returning an Err if the length is not exactly 1.
/// get a single ast node from a list of asts, returning an Err if the length is not exactly 1. (This is used for parsing literals)
pub fn require_single_toplevel(span: Span, mut asts: Vec<Ast>) -> AstResult<Ast> {
match asts.len() {
0 => Err(AstError::MissingNode(span)),
Expand Down

0 comments on commit 8877b8f

Please sign in to comment.