algorithmify_macros 0.1.1

Macros for the algorithmify library
Documentation
use proc_macro::TokenTree;

use crate::{
    expression_mapper::{map_expression, map_reference, try_get_indexed_access},
    token_iterator::TokenIterator,
};

#[derive(Debug)]
pub(crate) struct StatementMapping {
    pub(crate) mapping: String,
}

pub(crate) fn map_statement(buffer: &mut String, iterator: &mut TokenIterator) {
    let result = [
        try_map_assignment,
        try_map_indexed_assignment,
        try_map_expression,
    ]
    .iter()
    .map(|f| execute_assignment_mapping(iterator, f))
    .find(|result| result.is_some())
    .and_then(|result| result);

    if let Some(result) = result {
        *buffer += result.mapping.as_str();
        *buffer += ",";
    } else {
        if let Some(tokens) = iterator.next_nth(5) {
            let reps = tokens.iter().map(|i| i.to_string()).collect::<Vec<_>>();
            panic!("statement not recognized: {}", reps.join(" "));
        } else {
            panic!("end of statement reached unexpectedly");
        }
    }
}

fn execute_assignment_mapping(
    iterator: &mut TokenIterator,
    mapping: &fn(&mut TokenIterator) -> Option<StatementMapping>,
) -> Option<StatementMapping> {
    let index = iterator.index;
    let result = mapping(iterator);
    if result.is_none() {
        iterator.rewind_to(index);
    }
    return result;
}

fn try_map_assignment(iterator: &mut TokenIterator) -> Option<StatementMapping> {
    iterator.try_get_next_token("let");
    iterator.try_get_next_token("mut");

    let identifier = if let TokenTree::Ident(ident) = iterator.next()? {
        map_reference(ident)
    } else {
        return None;
    };

    iterator.try_get_next_token("=")?;
    let expression = map_expression(iterator)?;

    iterator.try_get_next_token(";")?;

    let mapping = format!(
        "algorithmify::expressions::Statement::Assignment({}, {})",
        identifier, expression.mapping
    );

    Some(StatementMapping { mapping })
}

fn try_map_indexed_assignment(iterator: &mut TokenIterator) -> Option<StatementMapping> {
    iterator.try_get_next_token("let");
    iterator.try_get_next_token("mut");

    let identifier = try_get_indexed_access(iterator)?;

    iterator.try_get_next_token("=")?;
    let expression = map_expression(iterator)?;

    iterator.try_get_next_token(";")?;

    let mapping = format!(
        "algorithmify::expressions::Statement::IndexedAssigment({}, {})",
        identifier, expression.mapping
    );

    Some(StatementMapping { mapping })
}

pub(crate) fn try_map_expression(iterator: &mut TokenIterator) -> Option<StatementMapping> {
    if let Some(expression) = map_expression(iterator) {
        if let (Some(_), Some(_), _) | (None, None, _) | (None, Some(_), false) = (
            iterator.try_get_next_token(";"),
            iterator.peek(),
            expression.needs_semicolon_unless_final,
        ) {
            let mapping = format!(
                "algorithmify::expressions::Statement::Expression({})",
                expression.mapping
            );
            return Some(StatementMapping { mapping });
        }
    }

    None
}