Skip to content

Commit

Permalink
feat: move range bounds related checks into range.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
lavafroth committed May 31, 2024
1 parent 3893cf8 commit 23fd31e
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 150 deletions.
189 changes: 61 additions & 128 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use itertools::Itertools;
use itertools::{iproduct, Itertools};
use pest::{iterators::Pair, Parser};
use pest_derive::Parser;
use range::Bounds;
use thiserror::Error;
mod range;
mod token;
use crate::token::{Key, KeyAttribute, Modifier, Token};
use crate::token::{Key, KeyAttribute, Modifier};

#[derive(Debug, Error)]
pub enum ParseError {
Expand Down Expand Up @@ -65,23 +67,6 @@ pub struct Definition {
key: Key,
}

impl TryFrom<Vec<Token>> for Definition {
type Error = ParseError;
fn try_from(mut value: Vec<Token>) -> Result<Self, Self::Error> {
let Some(Token::Key(key)) = value.pop() else {
return Err(ParseError::Definition);
};
let modifiers = value
.into_iter()
.filter_map(|m| match m {
Token::Modifier(modifier) => Some(modifier),
_ => None,
})
.collect();
Ok(Definition { modifiers, key })
}
}

#[derive(Debug)]
pub struct Binding {
pub definition: Definition,
Expand All @@ -102,61 +87,76 @@ fn unescape(s: &str) -> &str {
&s[1..]
}

fn parse_key(component: Pair<'_, Rule>) -> Token {
fn parse_key(component: Pair<'_, Rule>) -> Key {
let mut attribute = KeyAttribute::None;
let mut key = String::default();
for inner in component.into_inner() {
match inner.as_rule() {
Rule::send => attribute |= KeyAttribute::Send,
Rule::on_release => attribute |= KeyAttribute::OnRelease,
Rule::key_base => key = pair_to_string(inner),
Rule::shorthand_allow => key = unescape(inner.as_str()).to_string(),
Rule::shorthand_allow | Rule::key_base => key = unescape(inner.as_str()).to_string(),
_ => {}
}
}
Token::new_key(key, attribute)
Key { key, attribute }
}

fn extract_trigger(component: Pair<'_, Rule>) -> Result<Vec<Token>, ParseError> {
let trigger = match component.as_rule() {
Rule::modifier => vec![Token::new_modifier(pair_to_string(component))],
Rule::modifier_shorthand | Rule::modifier_omit_shorthand => component
.into_inner()
.map(|component| Token::new_modifier(pair_to_string(component)))
.collect(),
Rule::shorthand => {
let mut keys = vec![];
for shorthand_component in component.into_inner() {
match shorthand_component.as_rule() {
Rule::key_in_shorthand => keys.push(parse_key(shorthand_component)),
Rule::key_range => {
let (lower_bound, upper_bound) = extract_bounds(shorthand_component)?;
keys.extend((lower_bound..=upper_bound).map(|key| {
Token::Key(Key {
#[derive(Default)]
pub struct DefinitionUncompiled {
pub modifiers: Vec<Vec<Modifier>>,
pub keys: Vec<Key>,
}

impl DefinitionUncompiled {
fn ingest(&mut self, component: Pair<'_, Rule>) -> Result<(), ParseError> {
match component.as_rule() {
Rule::modifier => self
.modifiers
.push(vec![Modifier(pair_to_string(component))]),
Rule::modifier_shorthand | Rule::modifier_omit_shorthand => self.modifiers.push(
component
.into_inner()
.map(|component| Modifier(pair_to_string(component)))
.collect(),
),
Rule::shorthand => {
for shorthand_component in component.into_inner() {
match shorthand_component.as_rule() {
Rule::key_in_shorthand => self.keys.push(parse_key(shorthand_component)),
Rule::key_range => {
let (lower_bound, upper_bound) =
Bounds::new(shorthand_component).expand_keys()?;
self.keys.extend((lower_bound..=upper_bound).map(|key| Key {
key: key.to_string(),
attribute: KeyAttribute::None,
})
}));
}));
}
_ => {}
}
_ => {}
}
}
keys
Rule::key_normal => self.keys.push(parse_key(component)),
_ => {}
};
Ok(())
}

fn compile(self) -> Vec<Definition> {
let mut defs = vec![];
let cartesian = self.modifiers.into_iter().multi_cartesian_product();
for (modifiers, key) in iproduct!(cartesian, self.keys) {
defs.push(Definition { modifiers, key });
}
Rule::key_in_shorthand | Rule::key_normal => vec![parse_key(component)],
_ => vec![],
};
Ok(trigger)
defs
}
}

fn unbind_parser(pair: Pair<'_, Rule>) -> Result<Vec<Definition>, ParseError> {
pair.into_inner()
.map(extract_trigger)
.collect::<Result<Vec<_>, ParseError>>()?
.into_iter()
.multi_cartesian_product()
.map(|u| u.try_into())
.collect()
let mut uncompiled = DefinitionUncompiled::default();
for thing in pair.into_inner() {
uncompiled.ingest(thing)?;
}
Ok(uncompiled.compile())
}

fn import_parser(pair: Pair<'_, Rule>) -> Vec<String> {
Expand All @@ -166,72 +166,14 @@ fn import_parser(pair: Pair<'_, Rule>) -> Vec<String> {
.collect()
}

fn extract_bounds(pair: Pair<'_, Rule>) -> Result<(char, char), ParseError> {
let mut bounds = pair.clone().into_inner();

// These unwraps must always work since the pest grammar picked up
// the pairs due to the presence of the lower and upper bounds.
// These should not be categorized as errors.
let lower_bound: char = bounds
.next()
.unwrap()
.as_str()
.parse()
.expect("failed to parse lower bound");
let upper_bound: char = bounds
.next()
.unwrap()
.as_str()
.parse()
.expect("failed to parse upper bound");

if !lower_bound.is_ascii() {
let err = pest::error::Error::new_from_span(
pest::error::ErrorVariant::<Rule>::CustomError {
message: format!(
"shorthand lower bound `{0}` is not an ASCII character",
lower_bound
),
},
pair.as_span(),
);
return Err(Box::new(err).into());
}
if !upper_bound.is_ascii() {
let err = pest::error::Error::new_from_span(
pest::error::ErrorVariant::<Rule>::CustomError {
message: format!(
"shorthand upper bound `{0}` is not an ASCII character",
upper_bound
),
},
pair.as_span(),
);
return Err(Box::new(err).into());
}
if lower_bound > upper_bound {
let err = pest::error::Error::new_from_span(
pest::error::ErrorVariant::<Rule>::CustomError {
message: format!(
"shorthand lower bound `{}` is greater than upper bound `{}`",
lower_bound, upper_bound
),
},
pair.as_span(),
);
return Err(Box::new(err).into());
}
Ok((lower_bound, upper_bound))
}

fn parse_command_shorthand(pair: Pair<'_, Rule>) -> Result<Vec<String>, ParseError> {
let mut command_variants = vec![];

for component in pair.into_inner() {
match component.as_rule() {
Rule::command_component => command_variants.push(pair_to_string(component)),
Rule::range => {
let (lower_bound, upper_bound) = extract_bounds(component)?;
let (lower_bound, upper_bound) = Bounds::new(component).expand_commands()?;
command_variants.extend((lower_bound..=upper_bound).map(|key| key.to_string()));
}
_ => {}
Expand All @@ -241,8 +183,8 @@ fn parse_command_shorthand(pair: Pair<'_, Rule>) -> Result<Vec<String>, ParseErr
}

fn binding_parser(pair: Pair<'_, Rule>) -> Result<Vec<Binding>, ParseError> {
let mut tokens = vec![];
let mut comm = vec![];
let mut uncompiled = DefinitionUncompiled::default();
for component in pair.clone().into_inner() {
match component.as_rule() {
Rule::command => {
Expand All @@ -258,24 +200,15 @@ fn binding_parser(pair: Pair<'_, Rule>) -> Result<Vec<Binding>, ParseError> {
}
}
}
_ => {
let trigger = extract_trigger(component)?;
if !trigger.is_empty() {
tokens.push(trigger);
}
}
_ => uncompiled.ingest(component)?,
}
}
let bind_cartesian_product: Vec<_> = tokens
let bind_cartesian_product = uncompiled.compile();
let command_cartesian_product = comm
.into_iter()
.multi_cartesian_product()
.map(Definition::try_from)
.collect::<Result<Vec<Definition>, ParseError>>()?;
let command_cartesian_product: Vec<_> = comm
.into_iter()
.multi_cartesian_product()
.map(|c| c.join(" "))
.collect();
.map(|c| c.join(""))
.collect_vec();
let bind_len = bind_cartesian_product.len();
let command_len = command_cartesian_product.len();

Expand Down
94 changes: 94 additions & 0 deletions src/range.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use crate::parse_key;
use crate::ParseError;
use crate::Rule;
use pest::iterators::Pair;
use pest::Span;

pub(crate) struct Bounds<'a> {
lower: Pair<'a, Rule>,
upper: Pair<'a, Rule>,
span: Span<'a>,
}

impl<'a> Bounds<'a> {
pub fn new(pair: Pair<'a, Rule>) -> Self {
let span = pair.as_span();
let mut iter = pair.into_inner();
Self {
lower: iter.next().unwrap().to_owned(),
upper: iter.next().unwrap().to_owned(),
span,
}
}
fn spanned_error(&self, message: String) -> ParseError {
let err = pest::error::Error::new_from_span(
pest::error::ErrorVariant::<Rule>::CustomError { message },
self.span,
);
Box::new(err).into()
}

pub fn expand_keys(&self) -> Result<(char, char), ParseError> {
let lower = parse_key(self.lower.clone());
let upper = parse_key(self.upper.clone());
// if range attributes are unequal, complain
if lower.attribute != upper.attribute {
return Err(
self.spanned_error("range bounds must have the same timing attributes".to_string())
);
}

let lower: char = lower
.key
.parse()
.expect("failed to parse lower bound as a character");
let upper: char = upper
.key
.parse()
.expect("failed to parse upper bound as a character");

self.verify_range_bounds(lower, upper)?;

Ok((lower, upper))
}
pub fn expand_commands(&self) -> Result<(char, char), ParseError> {
// These unwraps must always work since the pest grammar picked up
// the pairs due to the presence of the lower and upper bounds.
// These should not be categorized as errors.
let lower_bound = self
.lower
.as_str()
.parse()
.expect("failed to parse lower bound as a character");
let upper_bound = self
.upper
.as_str()
.parse()
.expect("failed to parse upper bound as a character");

self.verify_range_bounds(lower_bound, upper_bound)?;

Ok((lower_bound, upper_bound))
}
fn verify_range_bounds(&self, lower_bound: char, upper_bound: char) -> Result<(), ParseError> {
if !lower_bound.is_ascii() {
return Err(self.spanned_error(format!(
"shorthand lower bound `{0}` is not an ASCII character",
lower_bound
)));
}
if !upper_bound.is_ascii() {
return Err(self.spanned_error(format!(
"shorthand upper bound `{0}` is not an ASCII character",
upper_bound
)));
}
if lower_bound > upper_bound {
return Err(self.spanned_error(format!(
"shorthand lower bound `{}` is greater than upper bound `{}`",
lower_bound, upper_bound
)));
}
Ok(())
}
}
23 changes: 1 addition & 22 deletions src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,9 @@ bitflags::bitflags! {
}

#[derive(Debug, Clone)]
pub enum Token {
Modifier(Modifier),
Key(Key),
}

#[derive(Debug, Clone)]
pub struct Modifier(String);
pub struct Modifier(pub String);
#[derive(Debug, Clone)]
pub struct Key {
pub key: String,
pub attribute: KeyAttribute,
}

impl Token {
pub fn new_key(key: String, attribute: KeyAttribute) -> Self {
Self::Key(Key { key, attribute })
}
pub fn new_key_from_string(key: String) -> Self {
Self::Key(Key {
key,
attribute: KeyAttribute::None,
})
}
pub fn new_modifier(modifier: String) -> Self {
Self::Modifier(Modifier(modifier))
}
}

0 comments on commit 23fd31e

Please sign in to comment.