glyph-parser 0.0.1

Python-like parser for the Glyph programming language
Documentation
//! Track indentation in Python-like code

use std::collections::VecDeque;

pub struct IndentTracker {
    indent_stack: Vec<usize>,
    pending_tokens: VecDeque<IndentToken>,
    at_line_start: bool,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum IndentToken {
    Indent,
    Dedent,
}

impl Default for IndentTracker {
    fn default() -> Self {
        Self {
            indent_stack: vec![0],
            pending_tokens: VecDeque::new(),
            at_line_start: true,
        }
    }
}

impl IndentTracker {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn handle_newline(&mut self) {
        self.at_line_start = true;
    }

    pub fn handle_whitespace(&mut self, spaces: usize) -> Option<VecDeque<IndentToken>> {
        if !self.at_line_start {
            return None;
        }

        self.at_line_start = false;
        let current_indent = *self.indent_stack.last().unwrap_or(&0);

        if spaces > current_indent {
            // Indent
            self.indent_stack.push(spaces);
            self.pending_tokens.push_back(IndentToken::Indent);
        } else if spaces < current_indent {
            // Dedent - might be multiple levels
            while let Some(&level) = self.indent_stack.last() {
                if level <= spaces {
                    break;
                }
                self.indent_stack.pop();
                self.pending_tokens.push_back(IndentToken::Dedent);
            }
        }
        // If spaces == current_indent, no indent/dedent token

        if self.pending_tokens.is_empty() {
            None
        } else {
            Some(self.pending_tokens.drain(..).collect())
        }
    }

    pub fn handle_non_whitespace(&mut self) -> Option<VecDeque<IndentToken>> {
        if self.at_line_start {
            self.at_line_start = false;
            // No indentation at line start means column 0
            self.handle_whitespace(0)
        } else {
            None
        }
    }

    pub fn finish(&mut self) -> VecDeque<IndentToken> {
        // Emit remaining dedents
        while self.indent_stack.len() > 1 {
            self.indent_stack.pop();
            self.pending_tokens.push_back(IndentToken::Dedent);
        }
        self.pending_tokens.drain(..).collect()
    }
}