shiftkit 0.2.0

A Rust library for building parsers and grammars
Documentation
//! Defines utilities for simplifying grammar rule creation

use crate::grammar::{AstNodeId, HasTokenType, Index, TokenId};

/// Helper struct for building reduction functions with a simpler API
pub struct ReductionList<'a, T, A> {
    indices: &'a [Index],
    tokens: &'a [T],
    nodes: &'a [A],
}

impl<'a, T: HasTokenType, A> ReductionList<'a, T, A> {
    /// Create a new `ReductionList` from the given indices, tokens, and nodes
    #[must_use]
    pub const fn new(indices: &'a [Index], tokens: &'a [T], nodes: &'a [A]) -> Self {
        Self {
            indices,
            tokens,
            nodes,
        }
    }

    /// Get a token ID at the given index
    #[must_use]
    pub fn token_id(&self, index: usize) -> TokenId {
        self.indices[index].as_token_id()
    }

    /// Get a token reference at the given index
    #[must_use]
    pub fn token(&self, index: usize) -> &'a T {
        &self.tokens[self.token_id(index).0]
    }

    /// Get an AST node ID at the given index
    #[must_use]
    pub fn node(&self, index: usize) -> AstNodeId {
        self.indices[index].as_ast_node_id()
    }

    /// Get an AST node reference at the given index
    #[must_use]
    pub fn ast_node(&self, index: usize) -> &'a A {
        &self.nodes[self.node(index).0]
    }
}

/// Macro to create an ergonomic reduction function
///
/// Usage:
/// ```ignore
/// rule!(|list| Passthrough(0))
/// rule!(|list| list.node(0))  // shorthand for passthrough
/// rule!(|list| MyNode::new(...))
/// ```
#[macro_export]
macro_rules! rule {
    // Passthrough variant
    (|$list:ident| Passthrough($index:expr)) => {
        |indices, _tokens, _nodes| {
            $crate::grammar::ReductionResult::Forward(indices[$index].as_ast_node_id())
        }
    };
    // Shorthand: just returning `node(i)` means passthrough
    // FIXME: what if `$list != $list2`?
    (|$list:ident| $list2:ident.node($index:expr)) => {
        |indices, _tokens, _nodes| {
            $crate::grammar::ReductionResult::Forward(indices[$index].as_ast_node_id())
        }
    };
    // General case: create new node
    (|$list:ident| $body:expr) => {
        |indices, tokens, nodes| {
            let $list = $crate::grammar::builder::ReductionList::new(indices, tokens, nodes);
            $crate::grammar::ReductionResult::NewNode($body)
        }
    };
}

/// Macro to extract a value from a token by pattern matching
#[macro_export]
macro_rules! extract {
    ($token:expr, $pattern:pat => $result:expr) => {
        match $token {
            $pattern => $result,
            _ => unreachable!("Token pattern mismatch"),
        }
    };
}