Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* 0.2.11
* FEATURE: ranges
* `padding` function
* `width` function
* 0.2.10
* BUGFIX: `on_mount` is now called after the children are generated
* BUGFIX: `on_tick` is now run before the cycle call
Expand Down
24 changes: 12 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "anathema"
edition = "2024"
version = "0.2.10"
version = "0.2.11"
license = "MIT"
description = "Create beautiful, easily customisable terminal applications"
keywords = ["tui", "terminal", "widgets", "ui", "layout"]
Expand Down Expand Up @@ -40,24 +40,24 @@ workspace = true

[workspace.package]
edition = "2024"
version = "0.2.10"
version = "0.2.11"

[workspace.dependencies]
bitflags = "2.4.1"
crossterm = "0.28.1"
unicode-width = "0.1.11"
flume = "0.11.0"
notify = "6.1.1"
anathema-default-widgets = { path = "./anathema-default-widgets", version = "0.2.10" }
anathema-backend = { path = "./anathema-backend", version = "0.2.10" }
anathema-runtime = { path = "./anathema-runtime", version = "0.2.10" }
anathema-state = { path = "./anathema-state", version = "0.2.10" }
anathema-state-derive = { path = "./anathema-state-derive", version = "0.2.10" }
anathema-store = { path = "./anathema-store", version = "0.2.10" }
anathema-templates = { path = "./anathema-templates", version = "0.2.10" }
anathema-widgets = { path = "./anathema-widgets", version = "0.2.10" }
anathema-geometry = { path = "./anathema-geometry", version = "0.2.10" }
anathema-value-resolver = { path = "./anathema-value-resolver", version = "0.2.10" }
anathema-default-widgets = { path = "./anathema-default-widgets", version = "0.2.11" }
anathema-backend = { path = "./anathema-backend", version = "0.2.11" }
anathema-runtime = { path = "./anathema-runtime", version = "0.2.11" }
anathema-state = { path = "./anathema-state", version = "0.2.11" }
anathema-state-derive = { path = "./anathema-state-derive", version = "0.2.11" }
anathema-store = { path = "./anathema-store", version = "0.2.11" }
anathema-templates = { path = "./anathema-templates", version = "0.2.11" }
anathema-widgets = { path = "./anathema-widgets", version = "0.2.11" }
anathema-geometry = { path = "./anathema-geometry", version = "0.2.11" }
anathema-value-resolver = { path = "./anathema-value-resolver", version = "0.2.11" }

[workspace]
members = [
Expand Down
9 changes: 6 additions & 3 deletions anathema-state/src/store/subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,18 +214,21 @@ impl Subscribers {
fn insert(&mut self, sub: Subscriber) {
match self {
Self::Empty => *self = Self::One(sub),
Self::One(key) => *self = Self::Arr([*key, sub, Subscriber::MAX], KeyIndex::TWO),
Self::One(key) if *key != sub => *self = Self::Arr([*key, sub, Subscriber::MAX], KeyIndex::TWO),
Self::Arr(arr_keys, index) if *index == KeyIndex::MAX => {
let mut keys = Vec::with_capacity(KeyIndex::max() + 1);
keys.extend_from_slice(arr_keys);
keys.push(sub);
*self = Self::Heap(keys);
}
Self::Arr(keys, index) => {
Self::Arr(keys, index) if !keys.contains(&sub) => {
keys[index.0 as usize] = sub;
index.add();
}
Self::Heap(keys) => keys.push(sub),
Self::Heap(keys) if !keys.contains(&sub) => keys.push(sub),

// The sub is already registered
Self::Arr(..) | Self::One(_) | Self::Heap(_) => (),
}
}

Expand Down
4 changes: 4 additions & 0 deletions anathema-templates/src/expressions/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ pub(super) fn eval(expr: Expr, strings: &Strings) -> Result<Expression, ParseErr
let rhs = strings.get_unchecked(string_id);
Expression::Index(lhs, Expression::Str(rhs).into())
}
Operator::DotDot => {
let (lhs, rhs) = (eval(*lhs, strings)?.into(), eval(*rhs, strings)?.into());
Expression::Range(lhs, rhs)
}
Operator::Mul | Operator::Plus | Operator::Minus | Operator::Div | Operator::Mod => {
let (lhs, rhs) = (eval(*lhs, strings)?.into(), eval(*rhs, strings)?.into());
let op = match op {
Expand Down
8 changes: 8 additions & 0 deletions anathema-templates/src/expressions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ pub enum Expression {
// Either
Either(Box<Self>, Box<Self>),

// Range
Range(Box<Self>, Box<Self>),

// Function call
Call { fun: Box<Self>, args: Vec<Self> },
}
Expand Down Expand Up @@ -121,6 +124,7 @@ impl Display for Expression {
write!(f, "{lhs} {op} {rhs}")
}
Self::Either(lhs, rhs) => write!(f, "{lhs} ? {rhs}"),
Self::Range(lhs, rhs) => write!(f, "{lhs} .. {rhs}"),
Self::List(list) => {
write!(
f,
Expand Down Expand Up @@ -192,6 +196,10 @@ pub fn either(lhs: Box<Expression>, rhs: Box<Expression>) -> Box<Expression> {
Expression::Either(lhs, rhs).into()
}

pub fn range(lhs: Box<Expression>, rhs: Box<Expression>) -> Box<Expression> {
Expression::Range(lhs, rhs).into()
}

// -----------------------------------------------------------------------------
// - Maths -
// -----------------------------------------------------------------------------
Expand Down
30 changes: 23 additions & 7 deletions anathema-templates/src/expressions/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ use crate::token::{Kind, Operator, Tokens, Value};
pub(crate) mod prec {
pub const INITIAL: u8 = 0;
pub const CONDITIONAL: u8 = 2;
pub const EQUALITY: u8 = 3;
pub const LOGICAL: u8 = 4;
pub const SUM: u8 = 5;
pub const PRODUCT: u8 = 6;
pub const PREFIX: u8 = 8;
pub const CALL: u8 = 10;
pub const SUBCRIPT: u8 = 11;
pub const RANGE: u8 = 3;
pub const EQUALITY: u8 = 4;
pub const LOGICAL: u8 = 5;
pub const SUM: u8 = 6;
pub const PRODUCT: u8 = 7;
pub const PREFIX: u8 = 9;
pub const CALL: u8 = 11;
pub const SUBCRIPT: u8 = 12;
}

fn get_precedence(op: Operator) -> u8 {
Expand All @@ -30,6 +31,7 @@ fn get_precedence(op: Operator) -> u8 {
}
Operator::EqualEqual | Operator::NotEqual => prec::EQUALITY,
Operator::Or | Operator::And | Operator::Either => prec::CONDITIONAL,
Operator::DotDot => prec::RANGE,

_ => prec::INITIAL,
}
Expand Down Expand Up @@ -384,4 +386,18 @@ mod test {
let actual = parse(input);
assert_eq!(actual, "(? (? <sid 1> <sid 2>) <sid 3>)");
}

#[test]
fn range() {
let input = "a..b";
let actual = parse(input);
assert_eq!(actual, "(.. <sid 1> <sid 2>)");
}

#[test]
fn range_2() {
let input = "1 + 0..1 + 5";
let actual = parse(input);
assert_eq!(actual, "(.. (+ 1 0) (+ 1 5))");
}
}
65 changes: 59 additions & 6 deletions anathema-templates/src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,36 @@ use crate::error::{ParseError, ParseErrorKind, Result};
use crate::strings::Strings;
use crate::token::{Kind, Operator, Token, Value};

// TODO:
// const EOF: char = '\0';
// * change the lexer to use this one instead.
// * split the spans and tokens
// * spans can point to tokens, know their lines and cols
// struct Cursor<'src> {
// chars: Peekable<Chars<'src>>,
// }

// impl<'src> Cursor<'src> {
// fn new(chars: Peekable<Chars<'src>>) -> Self {
// Self { chars }
// }

// fn next(&mut self) -> char {
// self.chars.next().unwrap_or(EOF)
// }

// fn first(&self) -> char {
// let mut iter = self.chars.clone();
// iter.next().unwrap_or(EOF)
// }

// fn second(&self) -> char {
// let mut iter = self.chars.clone();
// iter.next();
// iter.next().unwrap_or(EOF)
// }
// }

impl<'src, 'consts> Iterator for Lexer<'src, 'consts> {
type Item = Result<Token>;

Expand Down Expand Up @@ -58,6 +88,10 @@ impl<'src, 'strings> Lexer<'src, 'strings> {
let _ = self.chars.next();
Ok(Kind::Op(Operator::And).to_token(index))
}
('.', Some('.')) => {
let _ = self.chars.next();
Ok(Kind::Op(Operator::DotDot).to_token(index))
}
('|', Some('|')) => {
let _ = self.chars.next();
Ok(Kind::Op(Operator::Or).to_token(index))
Expand Down Expand Up @@ -179,14 +213,27 @@ impl<'src, 'strings> Lexer<'src, 'strings> {
let mut end = index;
let mut parse_float = &self.src[index..=index] == ".";

let _signed = &self.src[index..=index] == "-" || self.chars.peek().map(|(_, c)| *c == '-').unwrap_or(false);
loop {
if let Some((e, '0'..='9')) = self.chars.peek() {
end = *e;
self.chars.next();
continue;
}

while let Some((e, c @ ('0'..='9' | '.'))) = self.chars.peek() {
if *c == '.' {
parse_float = true;
if let Some((_, '.')) = self.chars.peek() {
let mut chars = self.chars.clone();
_ = chars.next();
// If the next character is a dot, then the following character has to be a number
// or this is not a valid float.
if let Some((e, '0'..='9')) = chars.peek() {
parse_float = true;
end = *e;
self.chars.next();
continue;
}
}
end = *e;
self.chars.next();

break;
}

let input = &self.src[index..=end];
Expand Down Expand Up @@ -527,4 +574,10 @@ mod test {
let decl = token_kind("?");
assert_eq!(decl, Kind::Op(Operator::Either));
}

#[test]
fn double_dot() {
let decl = token_kind("..");
assert_eq!(decl, Kind::Op(Operator::DotDot));
}
}
13 changes: 12 additions & 1 deletion anathema-templates/src/statements/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub(crate) fn const_eval(expr: impl Into<Expression>, ctx: &Context<'_>) -> Opti
E::Negative(expr) => E::Negative(ce!(*expr)),
E::Equality(lhs, rhs, eq) => E::Equality(ce!(*lhs), ce!(*rhs), eq),
E::LogicalOp(lhs, rhs, op) => E::LogicalOp(ce!(*lhs), ce!(*rhs), op),
E::Range(from, to) => E::Range(ce!(*from), ce!(*to)),

E::Ident(_) | E::Index(..) => eval_path(expr, ctx)?,
E::Variable(_) => unreachable!("const eval is not recursive so this can never happen"),
Expand Down Expand Up @@ -100,7 +101,7 @@ pub(crate) fn const_eval(expr: impl Into<Expression>, ctx: &Context<'_>) -> Opti
#[cfg(test)]
mod test {
use super::*;
use crate::expressions::{add, div, either, ident, index, list, mul, num, strlit, sub};
use crate::expressions::{add, div, either, ident, index, list, mul, num, range, strlit, sub};
use crate::statements::with_context;

#[test]
Expand Down Expand Up @@ -177,4 +178,14 @@ mod test {
assert_eq!(output, *expr);
});
}

#[test]
fn const_range() {
with_context(|ctx| {
let expr = range(num(1), num(2));

let output = const_eval(expr, &ctx).unwrap();
assert_eq!(output, *range(num(1), num(2)));
});
}
}
9 changes: 8 additions & 1 deletion anathema-templates/src/statements/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ impl Iterator for Parser<'_, '_, '_> {
mod test {
use super::*;
use crate::error::ErrorKind;
use crate::expressions::{boolean, ident, map, num, strlit, text_segments};
use crate::expressions::{boolean, ident, map, num, range, strlit, text_segments};
use crate::lexer::Lexer;
use crate::statements::test::{
associated_fun, case, component, else_stmt, eof, for_loop, global, if_else, if_stmt, load_attrib, load_value,
Expand Down Expand Up @@ -977,6 +977,13 @@ mod test {
);
}

#[test]
fn parse_range() {
let src = "for i in x..y";
let mut statements = parse_ok(src);
assert_eq!(statements.remove(0), for_loop(2, range(ident("x"), ident("y"))));
}

#[test]
fn parse_local_declaration() {
let src = "let x = 1";
Expand Down
2 changes: 2 additions & 0 deletions anathema-templates/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub enum Operator {
And,
Or,
Dot,
DotDot,
Comma,
Colon,
Semicolon,
Expand Down Expand Up @@ -66,6 +67,7 @@ impl Display for Operator {
Self::And => write!(f, "&&"),
Self::Or => write!(f, "||"),
Self::Dot => write!(f, "."),
Self::DotDot => write!(f, ".."),
Self::Comma => write!(f, ","),
Self::Colon => write!(f, ":"),
Self::Semicolon => write!(f, ";"),
Expand Down
2 changes: 1 addition & 1 deletion anathema-testutils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ homepage = "https://github.com/togglebyte/anathema"
repository = "https://github.com/togglebyte/anathema"

[dependencies]
anathema = { path = "../", version = "0.2.10" }
anathema = { path = "../", version = "0.2.11" }

[lints]
workspace = true
14 changes: 14 additions & 0 deletions anathema-value-resolver/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub enum ValueExpr<'bp> {
List(Box<[Self]>),
Map(HashMap<&'bp str, Self>),
Index(Box<Self>, Box<Self>),
Range(Box<Self>, Box<Self>),
Attributes(Key),

Not(Box<Self>),
Expand Down Expand Up @@ -236,6 +237,18 @@ pub(crate) fn resolve_value<'a, 'bp>(
}
resolve_value(second, ctx)
}
ValueExpr::Range(from, to) => {
let from = match resolve_int(from, ctx) {
Some(i) => i,
None => return ValueKind::Null,
};

let to = match resolve_int(to, ctx) {
Some(i) => i,
None => return ValueKind::Null,
};
ValueKind::Range(from, to)
}

// -----------------------------------------------------------------------------
// - Maps, lists and maybe -
Expand Down Expand Up @@ -437,6 +450,7 @@ fn resolve_int<'bp>(index: &ValueExpr<'bp>, ctx: &mut ValueResolutionContext<'_,
| ValueKind::DynMap(_)
| ValueKind::Attributes
| ValueKind::List(_)
| ValueKind::Range(..)
| ValueKind::DynList(_) => None,
}
}
Expand Down
2 changes: 2 additions & 0 deletions anathema-value-resolver/src/functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ impl FunctionTable {
inner.insert("to_lower".into(), Function::from(string::to_lower));
inner.insert("truncate".into(), Function::from(string::truncate));
inner.insert("to_str".into(), Function::from(string::to_str));
inner.insert("pad".into(), Function::from(string::pad));
inner.insert("width".into(), Function::from(string::width));
inner.insert("to_int".into(), Function::from(number::to_int));
inner.insert("to_float".into(), Function::from(number::to_float));
inner.insert("round".into(), Function::from(number::round));
Expand Down
1 change: 1 addition & 0 deletions anathema-value-resolver/src/functions/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub(super) fn to_int<'bp>(args: &[ValueKind<'bp>]) -> ValueKind<'bp> {
| ValueKind::List(_)
| ValueKind::DynList(_)
| ValueKind::DynMap(_)
| ValueKind::Range(..)
| ValueKind::Composite(_) => ValueKind::Null,
}
}
Expand Down
Loading