use std::collections::{HashMap, HashSet};
use std::path::Path;

use crate::ir::{BinOp, CircBuf, CircBufLayout, Expr, HelperDef, HelperParam, Statement, VarType};

// --- Public API ---

#[derive(Debug, Clone)]
pub struct ParsedCSource {
    pub lookback_body: Vec<Statement>,
    pub functions: Vec<ParsedCFunction>,
    /// File-level comment blocks that preceded the first function (e.g. the
    /// `List of contributors` / `Change history` block). Each entry is one
    /// comment block's cleaned content lines.
    pub header_comments: Vec<Vec<String>>,
}

#[derive(Debug, Clone)]
pub struct ParsedCFunction {
    pub name: String,
    pub body: Vec<Statement>,
    /// Parameter names and C types, in order. E.g., [("startIdx", "int"), ("inReal", "const double *")].
    pub params: Vec<(String, String)>,
}

/// Parse a `.c` source file containing TA-Lib function definitions.
pub fn parse_c_source(path: &Path) -> ParsedCSource {
    let input = std::fs::read_to_string(path)
        .unwrap_or_else(|e| panic!("Failed to read {}: {}", path.display(), e));
    parse_c_source_str(&input)
}

/// Parse C source from a string.
pub fn parse_c_source_str(source: &str) -> ParsedCSource {
    let (tokens, comments) = tokenize_with_comments(source);
    extract_functions(&tokens, &comments)
}

/// Parse a helper C file containing standalone utility functions.
/// Returns a `Vec<HelperDef>` — one entry per function found in the file.
pub fn parse_helper_file(path: &Path) -> Vec<HelperDef> {
    let input = std::fs::read_to_string(path)
        .unwrap_or_else(|e| panic!("Failed to read {}: {}", path.display(), e));
    parse_helper_file_str(&input)
}

/// Parse helper functions from a string of C source code.
pub fn parse_helper_file_str(source: &str) -> Vec<HelperDef> {
    let tokens = tokenize(source);
    extract_helper_functions(&tokens)
}

// --- Tokenizer ---

#[derive(Debug, Clone, PartialEq)]
enum Token {
    Ident(String),
    Number(f64),
    IntNumber(i64),
    Op(String),
    Star,
    Ampersand,
    LBrace,
    RBrace,
    LBracket,
    RBracket,
    LParen,
    RParen,
    Bang,
    Semicolon,
    Comma,
    Colon,
    PlusPlus,
    MinusMinus,
    Question,
    Dot,
    Pipe,
}

/// Strip #define and #undef lines (including multi-line continuations with \)
fn strip_local_macros(input: &str) -> String {
    let mut result = Vec::new();
    let mut in_macro = false;
    for line in input.lines() {
        let trimmed = line.trim();
        if in_macro {
            // Continue skipping until a line doesn't end with backslash
            in_macro = trimmed.ends_with('\\');
            continue;
        }
        if trimmed.starts_with("#define ")
            || trimmed.starts_with("#define\t")
            || trimmed.starts_with("#undef ")
            || trimmed.starts_with("#undef\t")
        {
            in_macro = trimmed.ends_with('\\');
            continue;
        }
        result.push(line);
    }
    result.join("\n")
}

/// Clean a raw comment body (the text between `//`..EOL or between `/*` and
/// `*/`) into content lines: leading indentation and a leading `*` (plus one
/// following space) are stripped from each line, trailing whitespace removed.
/// This preserves interior alignment (e.g. ADX's ASCII-art `+DM/-DM` diagram)
/// so a backend can faithfully re-wrap it in ` * `-prefixed form.
fn clean_comment_lines(raw: &str) -> Vec<String> {
    let mut lines: Vec<String> = raw
        .split('\n')
        .map(|line| {
            let t = line.trim_end().trim_start();
            let t = t
                .strip_prefix('*')
                .map_or(t, |r| r.strip_prefix(' ').unwrap_or(r));
            t.to_string()
        })
        .collect();
    // Drop trailing blank lines — chiefly the empty line produced when the
    // closing `*/` sits on its own line (its leading whitespace). A leading
    // blank (from `/*` on its own line) is kept, as it is meaningful layout.
    while lines.last().is_some_and(std::string::String::is_empty) {
        lines.pop();
    }
    lines
}

/// Tokenize, dropping comments — the historical API used throughout the tests.
fn tokenize(input: &str) -> Vec<Token> {
    tokenize_with_comments(input).0
}

/// Tokenize while capturing comments on the side. The returned token stream is
/// byte-for-byte identical to [`tokenize`] (comment-free, so every existing
/// parser path is unaffected); the second element lists each comment paired with
/// the index of the token it immediately precedes (`tokens.len()` at capture
/// time), plus its cleaned content lines. The parser re-associates these to
/// statements positionally without ever seeing a comment token.
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
fn tokenize_with_comments(input: &str) -> (Vec<Token>, Vec<(usize, Vec<String>, bool)>) {
    let input = strip_local_macros(input);
    let mut tokens = Vec::new();
    // (index of the token this comment precedes, cleaned lines, trailing?).
    // `trailing` = the comment shares a source line with the preceding token
    // (no newline since) — i.e. `x && // note` vs a comment on its own line.
    let mut comments: Vec<(usize, Vec<String>, bool)> = Vec::new();
    let chars: Vec<char> = input.chars().collect();
    let mut i = 0;
    let mut newline_since_token = true;
    let mut last_token_count = 0usize;

    while i < chars.len() {
        // A token was emitted in the previous iteration → reset the newline flag.
        if tokens.len() != last_token_count {
            newline_since_token = false;
            last_token_count = tokens.len();
        }

        let c = chars[i];

        // Skip whitespace (track newlines for trailing-comment detection)
        if c == '\n' {
            newline_since_token = true;
            i += 1;
            continue;
        }
        if c == ' ' || c == '\t' || c == '\r' {
            i += 1;
            continue;
        }

        // Capture single-line comments (anchored to the next token)
        if c == '/' && i + 1 < chars.len() && chars[i + 1] == '/' {
            let start = i + 2;
            while i < chars.len() && chars[i] != '\n' {
                i += 1;
            }
            let text: String = chars[start..i].iter().collect();
            comments.push((tokens.len(), clean_comment_lines(&text), !newline_since_token));
            continue;
        }

        // Capture multi-line comments (anchored to the next token)
        if c == '/' && i + 1 < chars.len() && chars[i + 1] == '*' {
            i += 2;
            let start = i;
            while i + 1 < chars.len() && !(chars[i] == '*' && chars[i + 1] == '/') {
                i += 1;
            }
            let end = i.min(chars.len());
            let text: String = chars[start..end].iter().collect();
            comments.push((tokens.len(), clean_comment_lines(&text), !newline_since_token));
            i += 2;
            continue;
        }

        // Semicolon
        if c == ';' {
            tokens.push(Token::Semicolon);
            i += 1;
            continue;
        }

        // Comma
        if c == ',' {
            tokens.push(Token::Comma);
            i += 1;
            continue;
        }

        // Colon
        if c == ':' {
            tokens.push(Token::Colon);
            i += 1;
            continue;
        }

        // Parentheses
        if c == '(' {
            tokens.push(Token::LParen);
            i += 1;
            continue;
        }
        if c == ')' {
            tokens.push(Token::RParen);
            i += 1;
            continue;
        }

        // Braces and brackets
        if c == '{' {
            tokens.push(Token::LBrace);
            i += 1;
            continue;
        }
        if c == '}' {
            tokens.push(Token::RBrace);
            i += 1;
            continue;
        }
        if c == '[' {
            tokens.push(Token::LBracket);
            i += 1;
            continue;
        }
        if c == ']' {
            tokens.push(Token::RBracket);
            i += 1;
            continue;
        }

        // Bang (!) — check for != first
        if c == '!' {
            if i + 1 < chars.len() && chars[i + 1] == '=' {
                tokens.push(Token::Op("!=".to_string()));
                i += 2;
                continue;
            }
            tokens.push(Token::Bang);
            i += 1;
            continue;
        }

        // Plus: ++, +=, or +
        if c == '+' {
            if i + 1 < chars.len() {
                if chars[i + 1] == '+' {
                    tokens.push(Token::PlusPlus);
                    i += 2;
                    continue;
                }
                if chars[i + 1] == '=' {
                    tokens.push(Token::Op("+=".to_string()));
                    i += 2;
                    continue;
                }
            }
            tokens.push(Token::Op("+".to_string()));
            i += 1;
            continue;
        }

        // Minus: --, -=, or -
        if c == '-' {
            if i + 1 < chars.len() {
                if chars[i + 1] == '-' {
                    tokens.push(Token::MinusMinus);
                    i += 2;
                    continue;
                }
                if chars[i + 1] == '=' {
                    tokens.push(Token::Op("-=".to_string()));
                    i += 2;
                    continue;
                }
            }
            tokens.push(Token::Op("-".to_string()));
            i += 1;
            continue;
        }

        // Star: *=, or *
        if c == '*' {
            if i + 1 < chars.len() && chars[i + 1] == '=' {
                tokens.push(Token::Op("*=".to_string()));
                i += 2;
                continue;
            }
            tokens.push(Token::Star);
            i += 1;
            continue;
        }

        // Slash: /= or /
        if c == '/' {
            if i + 1 < chars.len() && chars[i + 1] == '=' {
                tokens.push(Token::Op("/=".to_string()));
                i += 2;
                continue;
            }
            tokens.push(Token::Op("/".to_string()));
            i += 1;
            continue;
        }

        // Percent: %
        if c == '%' {
            tokens.push(Token::Op("%".to_string()));
            i += 1;
            continue;
        }

        // Two-char operators starting with <, >, =, &, |
        if c == '<' {
            if i + 1 < chars.len() && chars[i + 1] == '=' {
                tokens.push(Token::Op("<=".to_string()));
                i += 2;
            } else if i + 1 < chars.len() && chars[i + 1] == '<' {
                tokens.push(Token::Op("<<".to_string()));
                i += 2;
            } else {
                tokens.push(Token::Op("<".to_string()));
                i += 1;
            }
            continue;
        }
        if c == '>' {
            if i + 1 < chars.len() && chars[i + 1] == '=' {
                tokens.push(Token::Op(">=".to_string()));
                i += 2;
            } else if i + 1 < chars.len() && chars[i + 1] == '>' {
                tokens.push(Token::Op(">>".to_string()));
                i += 2;
            } else {
                tokens.push(Token::Op(">".to_string()));
                i += 1;
            }
            continue;
        }
        if c == '=' {
            if i + 1 < chars.len() && chars[i + 1] == '=' {
                tokens.push(Token::Op("==".to_string()));
                i += 2;
            } else {
                tokens.push(Token::Op("=".to_string()));
                i += 1;
            }
            continue;
        }
        if c == '&' && i + 1 < chars.len() && chars[i + 1] == '&' {
            tokens.push(Token::Op("&&".to_string()));
            i += 2;
            continue;
        }
        if c == '&' {
            tokens.push(Token::Ampersand);
            i += 1;
            continue;
        }
        if c == '|' {
            if i + 1 < chars.len() && chars[i + 1] == '|' {
                tokens.push(Token::Op("||".to_string()));
                i += 2;
                continue;
            }
            tokens.push(Token::Pipe);
            i += 1;
            continue;
        }

        // Numbers (including leading-dot floats like .0)
        if c.is_ascii_digit() || (c == '.' && i + 1 < chars.len() && chars[i + 1].is_ascii_digit())
        {
            let start = i;
            let mut is_float = c == '.';
            if c == '.' {
                // Leading-dot float: .0, .5, etc.
                i += 1;
            }
            while i < chars.len() && chars[i].is_ascii_digit() {
                i += 1;
            }
            if !is_float && i < chars.len() && chars[i] == '.' {
                is_float = true;
                i += 1;
                while i < chars.len() && chars[i].is_ascii_digit() {
                    i += 1;
                }
            }
            if is_float {
                // Skip trailing 'f' suffix (e.g., 0.0f)
                if i < chars.len() && chars[i] == 'f' {
                    i += 1;
                }
                let s: String = chars[start..i].iter().collect();
                let cleaned = s.trim_end_matches('f');
                tokens.push(Token::Number(cleaned.parse().unwrap()));
            } else {
                let s: String = chars[start..i].iter().collect();
                tokens.push(Token::IntNumber(s.parse().unwrap()));
            }
            continue;
        }

        // Identifiers
        if c.is_ascii_alphabetic() || c == '_' {
            let start = i;
            while i < chars.len() && (chars[i].is_ascii_alphanumeric() || chars[i] == '_') {
                i += 1;
            }
            let s: String = chars[start..i].iter().collect();
            tokens.push(Token::Ident(s));
            continue;
        }

        // Question mark (ternary operator)
        if c == '?' {
            tokens.push(Token::Question);
            i += 1;
            continue;
        }

        // Dot: struct member access (e.g., arr[idx].field)
        if c == '.' {
            tokens.push(Token::Dot);
            i += 1;
            continue;
        }

        // Hash: skip any remaining preprocessor directives
        if c == '#' {
            while i < chars.len() && chars[i] != '\n' {
                i += 1;
            }
            continue;
        }

        panic!("Unexpected character: {c:?} at position {i}");
    }

    (tokens, comments)
}

// --- Function Extraction ---

/// Scan tokens for function definitions and extract their bodies.
/// Extract C parameter list from tokens starting at the expected `(` position.
/// Returns Vec of (param_name, c_type_string). Handles `const`, `*`, `[]`.
fn extract_func_params(tokens: &[Token], start: usize) -> Vec<(String, String)> {
    let mut params = Vec::new();
    let mut i = start;

    // Find opening paren
    while i < tokens.len() && tokens[i] != Token::LParen {
        i += 1;
    }
    if i >= tokens.len() {
        return params;
    }
    i += 1; // skip LParen

    // Parse params until RParen
    while i < tokens.len() && tokens[i] != Token::RParen {
        if tokens[i] == Token::Comma {
            i += 1;
            continue;
        }

        // Collect type tokens until we hit an identifier followed by comma/rparen/lbracket
        let mut type_parts = Vec::new();
        let mut param_name = String::new();

        // Consume tokens for this parameter
        while i < tokens.len() && tokens[i] != Token::Comma && tokens[i] != Token::RParen {
            match &tokens[i] {
                Token::Ident(s) => {
                    // Peek ahead: if next is comma, rparen, or lbracket, this is the name
                    let next = tokens.get(i + 1);
                    let is_name = matches!(
                        next,
                        Some(Token::Comma | Token::RParen | Token::LBracket) | None
                    );
                    if is_name && !type_parts.is_empty() {
                        param_name.clone_from(s);
                        i += 1;
                        // Skip [] or [N] if present (e.g., double buf[3])
                        if matches!(tokens.get(i), Some(Token::LBracket)) {
                            type_parts.push("[]".to_string());
                            i += 1; // skip [
                            // Consume everything until closing ]
                            while i < tokens.len() && tokens[i] != Token::RBracket {
                                i += 1;
                            }
                            if matches!(tokens.get(i), Some(Token::RBracket)) {
                                i += 1; // skip ]
                            }
                        }
                        break;
                    }
                    type_parts.push(s.clone());
                }
                Token::Star => type_parts.push("*".to_string()),
                _ => {}
            }
            i += 1;
        }

        if !param_name.is_empty() {
            params.push((param_name, type_parts.join(" ")));
        }
    }

    params
}

/// Collect the comments that fall inside a function body's token span
/// `[brace_start+1 ..= brace_end]`, re-based to slice-local indices (so index 0
/// is the first body token). The upper bound is inclusive: a comment whose next
/// token is the closing `}` at `brace_end` sits just before it, inside the body.
fn body_comments(
    comments: &[(usize, Vec<String>, bool)],
    brace_start: usize,
    brace_end: usize,
) -> Vec<(usize, Vec<String>, bool)> {
    let lo = brace_start + 1;
    comments
        .iter()
        .filter(|(a, _, _)| *a >= lo && *a <= brace_end)
        .map(|(a, lines, t)| (a - lo, lines.clone(), *t))
        .collect()
}

fn extract_functions(tokens: &[Token], comments: &[(usize, Vec<String>, bool)]) -> ParsedCSource {
    let mut lookback_body = Vec::new();
    let mut functions = Vec::new();
    let mut first_func_tok: Option<usize> = None;
    let mut i = 0;

    while i < tokens.len() {
        // Look for lookback function:
        //   Old: int TA_XXX_Lookback ( ... ) { body }
        //   New: int xxx_lookback ( ... ) { body }
        if matches!(&tokens[i], Token::Ident(s) if s == "int") {
            if let Some(Token::Ident(name)) = tokens.get(i + 1) {
                let is_old_lookback = name.contains("_Lookback") && name.starts_with("TA_");
                let is_new_lookback = name.ends_with("_lookback");
                if is_old_lookback || is_new_lookback {
                    // Skip to opening brace
                    if let Some(brace_start) = find_open_brace(tokens, i + 2) {
                        if let Some(brace_end) = find_matching_brace(tokens, brace_start) {
                            first_func_tok.get_or_insert(i);
                            let body_tokens = &tokens[brace_start + 1..brace_end];
                            let body_cmts = body_comments(comments, brace_start, brace_end);
                            lookback_body = parse_body(body_tokens, &body_cmts);
                            i = brace_end + 1;
                            continue;
                        }
                    }
                }
            }
        }

        // Look for implementation function:
        //   Old: TA_RetCode TA_XXX ( ... ) { body }
        //   New: TA_RetCode xxx ( ... ) { body }  (plain indicator name)
        //   Also: static TA_RetCode xxx ( ... ) { body }
        let func_start = if matches!(&tokens[i], Token::Ident(s) if s == "TA_RetCode") {
            Some(i)
        } else if matches!(&tokens[i], Token::Ident(s) if s == "static")
            && matches!(tokens.get(i + 1), Some(Token::Ident(s)) if s == "TA_RetCode")
        {
            Some(i + 1)
        } else {
            None
        };
        if let Some(ret_pos) = func_start {
            if let Some(Token::Ident(name)) = tokens.get(ret_pos + 1) {
                let is_old_func = name.starts_with("TA_");
                let is_new_func = !name.starts_with("TA_") && !name.ends_with("_lookback");
                if is_old_func || is_new_func {
                    let func_name = name.clone();
                    // Parse parameter list
                    let params = extract_func_params(tokens, ret_pos + 2);
                    if let Some(brace_start) = find_open_brace(tokens, ret_pos + 2) {
                        if let Some(brace_end) = find_matching_brace(tokens, brace_start) {
                            first_func_tok.get_or_insert(i);
                            let body_tokens = &tokens[brace_start + 1..brace_end];
                            let body_cmts = body_comments(comments, brace_start, brace_end);
                            functions.push(ParsedCFunction {
                                name: func_name,
                                body: parse_body(body_tokens, &body_cmts),
                                params,
                            });
                            i = brace_end + 1;
                            continue;
                        }
                    }
                }
            }
        }

        i += 1;
    }

    // File-level comments preceding the first function (e.g. contributors/history).
    // `<=` because a comment immediately before the first function's first token
    // anchors *at* that token's index; it is still a file-level header comment
    // (body comments all anchor past the opening brace, so the two never overlap).
    let header_start = first_func_tok.unwrap_or(0);
    let header_comments = comments
        .iter()
        .filter(|(a, _, _)| *a <= header_start && first_func_tok.is_some())
        .map(|(_, lines, _)| lines.clone())
        .collect();

    ParsedCSource {
        lookback_body,
        functions,
        header_comments,
    }
}

/// Scan tokens for standalone helper function definitions (not indicator functions).
/// Each helper has the form: `return_type name(params...) { body }`
fn extract_helper_functions(tokens: &[Token]) -> Vec<HelperDef> {
    let mut helpers = Vec::new();
    let mut i = 0;

    while i < tokens.len() {
        // Look for a return type keyword: `double` or `int`
        let return_type = match &tokens[i] {
            Token::Ident(s) if s == "double" => VarType::Real,
            Token::Ident(s) if s == "int" => VarType::Integer,
            _ => {
                i += 1;
                continue;
            }
        };

        // Next token must be the function name (an identifier)
        let Some(Token::Ident(name)) = tokens.get(i + 1) else {
            i += 1;
            continue;
        };
        let name = name.clone();

        // Next must be `(`
        if tokens.get(i + 2) != Some(&Token::LParen) {
            i += 1;
            continue;
        }

        // Parse parameter list between `(` and `)`
        let mut pi = i + 3; // skip return_type, name, LParen
        let mut params = Vec::new();
        while pi < tokens.len() && tokens[pi] != Token::RParen {
            // Skip commas
            if tokens[pi] == Token::Comma {
                pi += 1;
                continue;
            }
            // Expect: type_keyword param_name
            let param_type = match &tokens[pi] {
                Token::Ident(s) if s == "double" => VarType::Real,
                Token::Ident(s) if s == "int" => VarType::Integer,
                _ => break,
            };
            pi += 1;
            let param_name = match &tokens[pi] {
                Token::Ident(s) => s.clone(),
                _ => break,
            };
            pi += 1;
            params.push(HelperParam {
                name: param_name,
                var_type: param_type,
            });
        }
        if pi >= tokens.len() || tokens[pi] != Token::RParen {
            i += 1;
            continue;
        }
        pi += 1; // skip RParen

        // Next must be `{` (function body)
        if tokens.get(pi) != Some(&Token::LBrace) {
            i += 1;
            continue;
        }

        if let Some(brace_end) = find_matching_brace(tokens, pi) {
            let body_tokens = &tokens[pi + 1..brace_end];
            let body = parse_body(body_tokens, &[]);
            helpers.push(HelperDef {
                name,
                return_type,
                params,
                body,
            });
            i = brace_end + 1;
        } else {
            i += 1;
        }
    }

    helpers
}

/// Find the next `{` token starting from position `start`.
fn find_open_brace(tokens: &[Token], start: usize) -> Option<usize> {
    let mut i = start;
    while i < tokens.len() {
        if tokens[i] == Token::LBrace {
            return Some(i);
        }
        i += 1;
    }
    None
}

/// Given the position of an opening `{`, find the matching `}`.
fn find_matching_brace(tokens: &[Token], open: usize) -> Option<usize> {
    let mut depth = 1;
    let mut i = open + 1;
    while i < tokens.len() {
        match &tokens[i] {
            Token::LBrace => depth += 1,
            Token::RBrace => {
                depth -= 1;
                if depth == 0 {
                    return Some(i);
                }
            }
            _ => {}
        }
        i += 1;
    }
    None
}

/// Parse a slice of tokens (a function body) into statements. `comments` are the
/// body-local comments (index re-based to the slice) captured by
/// [`tokenize_with_comments`]; they are flushed positionally as
/// [`Statement::Comment`] nodes.
fn parse_body(tokens: &[Token], comments: &[(usize, Vec<String>, bool)]) -> Vec<Statement> {
    let mut parser = Parser::with_comments(tokens.to_vec(), comments.to_vec());
    parser.parse_statements()
}

// --- Parser ---

/// Map a C scalar element type token to a [`VarType`] (`int` -> Integer, else Real).
fn c_type_to_vartype(type_name: &str) -> VarType {
    if type_name == "int" {
        VarType::Integer
    } else {
        VarType::Real
    }
}

struct Parser {
    tokens: Vec<Token>,
    pos: usize,
    /// Comments captured by [`tokenize_with_comments`], each paired with the
    /// token index it precedes (in this body's re-based token space). Consumed
    /// in order via `comment_idx` and flushed as [`Statement::Comment`] at
    /// statement boundaries in [`parse_statements`](Parser::parse_statements).
    comments: Vec<(usize, Vec<String>, bool)>,
    comment_idx: usize,
    /// Function-local `typedef struct {...} Name;` field lists, for CIRCBUF `*_CLASS`.
    struct_defs: HashMap<String, Vec<(String, VarType)>>,
    /// CIRCBUF id -> element layout, captured at PROLOG so INIT/INIT_LOCAL_ONLY/DESTROY
    /// can carry the layout without a cross-statement lookup.
    circbufs: HashMap<String, CircBufLayout>,
}

impl Parser {
    #[cfg(test)]
    fn new(tokens: Vec<Token>) -> Self {
        Self::with_comments(tokens, Vec::new())
    }

    fn with_comments(tokens: Vec<Token>, comments: Vec<(usize, Vec<String>, bool)>) -> Self {
        Self {
            tokens,
            pos: 0,
            comments,
            comment_idx: 0,
            struct_defs: HashMap::new(),
            circbufs: HashMap::new(),
        }
    }

    /// Emit a [`Statement::Comment`] for every buffered comment whose anchor
    /// (the token it precedes) is at or before `pos_limit`, in source order.
    /// Called at statement boundaries so comments land immediately before the
    /// statement that followed them in the source.
    fn flush_comments(&mut self, pos_limit: usize, stmts: &mut Vec<Statement>) {
        while self.comment_idx < self.comments.len()
            && self.comments[self.comment_idx].0 <= pos_limit
        {
            let lines = self.comments[self.comment_idx].1.clone();
            stmts.push(Statement::Comment(lines));
            self.comment_idx += 1;
        }
    }

    fn expect_ident(&mut self) -> String {
        match self.advance() {
            Token::Ident(s) => s,
            other => panic!("Expected identifier, got {other:?}"),
        }
    }

    fn expect_int_literal(&mut self) -> i64 {
        match self.advance() {
            Token::IntNumber(n) => n,
            other => panic!("CIRCBUF static size must be an int literal, got {other:?}"),
        }
    }

    /// Resolve the element layout for a CIRCBUF macro: `*_CLASS` looks up the typedef'd
    /// struct fields (field-split storage); otherwise a scalar `Plain` layout.
    fn circbuf_layout(&self, macro_name: &str, type_name: &str) -> CircBufLayout {
        if macro_name.ends_with("_CLASS") {
            let fields = self.struct_defs.get(type_name).cloned().unwrap_or_else(|| {
                panic!(
                    "CIRCBUF *_CLASS: unknown struct '{type_name}' \
                     (typedef must precede PROLOG_CLASS in the function body)"
                )
            });
            CircBufLayout::Class(fields)
        } else {
            CircBufLayout::Plain(c_type_to_vartype(type_name))
        }
    }

    /// Parse a function-local `typedef struct { <type> <field>; ... } Name;` and record its
    /// fields in `struct_defs` for CIRCBUF `*_CLASS`. Emits no code (storage is the Prolog's).
    fn parse_typedef_struct(&mut self) -> Statement {
        self.advance(); // typedef
        match self.advance() {
            Token::Ident(ref s) if s == "struct" => {}
            other => panic!("Expected 'struct' after typedef, got {other:?}"),
        }
        self.expect(&Token::LBrace);
        let mut fields = Vec::new();
        while self.peek() != Some(&Token::RBrace) {
            let ty = c_type_to_vartype(&self.expect_ident());
            let fname = self.expect_ident();
            self.consume_semicolon();
            fields.push((fname, ty));
        }
        self.expect(&Token::RBrace);
        let name = self.expect_ident();
        self.consume_semicolon();
        self.struct_defs.insert(name, fields);
        Statement::Block { body: vec![] }
    }

    fn peek(&self) -> Option<&Token> {
        self.tokens.get(self.pos)
    }

    fn advance(&mut self) -> Token {
        let tok = self.tokens[self.pos].clone();
        self.pos += 1;
        tok
    }

    fn expect_op(&mut self, op: &str) {
        match self.advance() {
            Token::Op(ref s) if s == op => {}
            other => panic!("Expected '{op}', got {other:?}"),
        }
    }

    fn expect(&mut self, expected: &Token) {
        let tok = self.advance();
        assert_eq!(&tok, expected, "Expected {expected:?}, got {tok:?}");
    }

    fn parse_statements(&mut self) -> Vec<Statement> {
        let mut stmts = Vec::new();
        let mut declared_vars: HashSet<String> = HashSet::new();
        while self.pos < self.tokens.len() {
            // Flush comments that precede the token at the current boundary so
            // they render immediately before the upcoming statement.
            self.flush_comments(self.pos, &mut stmts);
            if self.peek() == Some(&Token::RBrace) {
                break;
            }
            // Skip stray semicolons
            if self.peek() == Some(&Token::Semicolon) {
                self.advance();
                continue;
            }
            match self.peek().cloned() {
                Some(Token::Ident(ref s)) if Self::is_type_keyword(s) => {
                    let decls = self.parse_var_decl();
                    Self::dedup_var_decls(decls, &mut declared_vars, &mut stmts);
                }
                Some(Token::Ident(ref s)) if s == "const" => {
                    self.advance(); // consume `const`
                    let decls = self.parse_var_decl();
                    Self::dedup_var_decls(decls, &mut declared_vars, &mut stmts);
                }
                _ => {
                    stmts.push(self.parse_statement());
                }
            }
        }
        // Flush trailing comments (e.g. just before this block's closing brace,
        // or at end of the top-level body where the loop exits without a boundary
        // check at the final position).
        self.flush_comments(self.pos, &mut stmts);
        stmts
    }

    /// Deduplicate variable declarations: if a name was already declared, convert
    /// re-declarations with an initializer into assignments, and drop bare re-declarations.
    fn dedup_var_decls(
        decls: Vec<Statement>,
        declared_vars: &mut HashSet<String>,
        stmts: &mut Vec<Statement>,
    ) {
        for decl in decls {
            match decl {
                Statement::VarDecl { ref name, ref init, .. } => {
                    if declared_vars.contains(name) {
                        if let Some(init_expr) = init.clone() {
                            stmts.push(Statement::Assign {
                                target: Expr::Var(name.clone()),
                                value: init_expr,
                                compound: false,
                            });
                        }
                        // If no initializer and already declared, skip entirely
                    } else {
                        declared_vars.insert(name.clone());
                        stmts.push(decl);
                    }
                }
                other => stmts.push(other),
            }
        }
    }

    fn parse_statement(&mut self) -> Statement {
        match self.peek().cloned() {
            Some(Token::Ident(ref s)) if s == "do" => self.parse_do_while(),
            Some(Token::Ident(ref s)) if s == "while" => self.parse_while(),
            Some(Token::Ident(ref s)) if s == "for" => self.parse_for(),
            Some(Token::Ident(ref s)) if s == "if" => self.parse_if(),
            Some(Token::Ident(ref s)) if s == "switch" => self.parse_switch(),
            Some(Token::Ident(ref s)) if s == "return" => self.parse_return(),
            Some(Token::Ident(ref s)) if s == "break" => {
                self.advance();
                self.consume_semicolon();
                Statement::Break
            }
            Some(Token::Ident(ref s)) if s == "continue" => {
                self.advance();
                self.consume_semicolon();
                Statement::Continue
            }
            Some(Token::Ident(ref s)) if s == "typedef" => self.parse_typedef_struct(),
            Some(Token::Ident(ref s)) if Self::is_macro_decl(s) => self.parse_macro_decl(),
            Some(Token::Star) => self.parse_pointer_deref_assign(),
            // (void)identifier; — suppress-unused-variable cast, treat as no-op
            Some(Token::LParen) => self.parse_void_cast_stmt(),
            // Anonymous block: { ... } — used for scoped variable declarations
            Some(Token::LBrace) => {
                self.advance(); // consume {
                let body = self.parse_statements();
                self.expect(&Token::RBrace);
                Statement::Block { body }
            }
            // Pre-increment as statement: ++x;
            Some(Token::PlusPlus) => {
                self.advance();
                let name = match self.advance() {
                    Token::Ident(s) => s,
                    other => panic!("Expected identifier after ++, got {other:?}"),
                };
                self.consume_semicolon();
                inc_dec_assign(name, BinOp::Add)
            }
            // Pre-decrement as statement: --x;
            Some(Token::MinusMinus) => {
                self.advance();
                let name = match self.advance() {
                    Token::Ident(s) => s,
                    other => panic!("Expected identifier after --, got {other:?}"),
                };
                self.consume_semicolon();
                inc_dec_assign(name, BinOp::Sub)
            }
            _ => self.parse_assignment_or_expr_stmt(),
        }
    }

    fn consume_semicolon(&mut self) {
        if self.peek() == Some(&Token::Semicolon) {
            self.advance();
        }
    }

    fn is_type_keyword(s: &str) -> bool {
        matches!(s, "int" | "double" | "size_t" | "TA_RetCode")
    }

    /// Check if an identifier is a known macro that acts as a declaration or standalone statement.
    fn is_macro_decl(s: &str) -> bool {
        matches!(
            s,
            "ENUM_DECLARATION"
                | "ARRAY_REF"
                | "ARRAY_ALLOC"
                | "ARRAY_FREE"
                | "CONSTANT_DOUBLE"
                | "CONSTANT_INTEGER"
                | "CIRCBUF_PROLOG"
                | "CIRCBUF_PROLOG_CLASS"
                | "CIRCBUF_INIT"
                | "CIRCBUF_INIT_CLASS"
                | "CIRCBUF_INIT_LOCAL_ONLY"
                | "CIRCBUF_DESTROY"
                | "CIRCBUF_NEXT"
                | "HILBERT_VARIABLES"
                | "INIT_HILBERT_VARIABLES"
                | "DO_HILBERT_ODD"
                | "DO_HILBERT_EVEN"
                | "DO_PRICE_WMA"
        )
    }

    /// Parse macro declarations/statements like:
    ///   `ENUM_DECLARATION(RetCode)` retCode;  -> `VarDecl` { type: `RetCodeType`, name: retCode }
    ///   `ARRAY_REF(buf)`;                      -> `VarDecl` { type: Real, name: buf }
    ///   `ARRAY_ALLOC(buf`, expr);              -> skip (no-op for code generation)
    ///   `ARRAY_FREE(buf)`;                     -> skip (no-op for code generation)
    #[allow(clippy::too_many_lines)]
    fn parse_macro_decl(&mut self) -> Statement {
        let macro_name = match self.advance() {
            Token::Ident(s) => s,
            other => panic!("Expected macro name, got {other:?}"),
        };
        self.expect(&Token::LParen);

        match macro_name.as_str() {
            "ENUM_DECLARATION" => {
                // ENUM_DECLARATION(RetCode) varName;
                let type_name = match self.advance() {
                    Token::Ident(s) => s,
                    other => panic!("Expected type name in ENUM_DECLARATION, got {other:?}"),
                };
                self.expect(&Token::RParen);
                let var_name = match self.advance() {
                    Token::Ident(s) => s,
                    other => panic!(
                        "Expected variable name after ENUM_DECLARATION({type_name}), got {other:?}"
                    ),
                };
                self.consume_semicolon();
                let var_type = if type_name == "RetCode" {
                    VarType::RetCodeType
                } else {
                    VarType::Integer
                };
                Statement::VarDecl {
                    var_type,
                    name: var_name,
                    init: None,
                }
            }
            "ARRAY_REF" => {
                // ARRAY_REF(buf);
                let var_name = match self.advance() {
                    Token::Ident(s) => s,
                    other => panic!("Expected variable name in ARRAY_REF, got {other:?}"),
                };
                self.expect(&Token::RParen);
                self.consume_semicolon();
                Statement::VarDecl {
                    var_type: VarType::Real,
                    name: var_name,
                    init: None,
                }
            }
            "ARRAY_ALLOC" | "ARRAY_FREE" => {
                // Skip all tokens until matching RParen, then semicolon (no-op for codegen).
                let mut depth = 1;
                while depth > 0 {
                    match self.advance() {
                        Token::LParen => depth += 1,
                        Token::RParen => depth -= 1,
                        _ => {}
                    }
                }
                self.consume_semicolon();
                Statement::Expr(Expr::FuncCall(macro_name, vec![]))
            }
            "CIRCBUF_PROLOG" | "CIRCBUF_PROLOG_CLASS" => {
                // CIRCBUF_PROLOG(Id, Type, StaticSize)
                let id = self.expect_ident();
                self.expect(&Token::Comma);
                let type_name = self.expect_ident();
                self.expect(&Token::Comma);
                let static_size = self.expect_int_literal();
                self.expect(&Token::RParen);
                self.consume_semicolon();
                let layout = self.circbuf_layout(&macro_name, &type_name);
                self.circbufs.insert(id.clone(), layout.clone());
                Statement::CircBuf(CircBuf::Prolog {
                    id,
                    layout,
                    static_size,
                })
            }
            "CIRCBUF_INIT" | "CIRCBUF_INIT_CLASS" => {
                // CIRCBUF_INIT(Id, Type, RuntimeSize)
                let id = self.expect_ident();
                self.expect(&Token::Comma);
                let _type_name = self.expect_ident();
                self.expect(&Token::Comma);
                let size = self.parse_expr();
                self.expect(&Token::RParen);
                self.consume_semicolon();
                let layout = self
                    .circbufs
                    .get(&id)
                    .cloned()
                    .unwrap_or_else(|| panic!("CIRCBUF_INIT before PROLOG for '{id}'"));
                Statement::CircBuf(CircBuf::Init { id, layout, size })
            }
            "CIRCBUF_INIT_LOCAL_ONLY" => {
                // CIRCBUF_INIT_LOCAL_ONLY(Id, Type)
                let id = self.expect_ident();
                self.expect(&Token::Comma);
                let _type_name = self.expect_ident();
                self.expect(&Token::RParen);
                self.consume_semicolon();
                let layout =
                    self.circbufs.get(&id).cloned().unwrap_or_else(|| {
                        panic!("CIRCBUF_INIT_LOCAL_ONLY before PROLOG for '{id}'")
                    });
                Statement::CircBuf(CircBuf::InitLocalOnly { id, layout })
            }
            "CIRCBUF_NEXT" => {
                let id = self.expect_ident();
                self.expect(&Token::RParen);
                self.consume_semicolon();
                Statement::CircBuf(CircBuf::Next { id })
            }
            "CIRCBUF_DESTROY" => {
                let id = self.expect_ident();
                self.expect(&Token::RParen);
                self.consume_semicolon();
                let layout = self
                    .circbufs
                    .get(&id)
                    .cloned()
                    .unwrap_or_else(|| panic!("CIRCBUF_DESTROY before PROLOG for '{id}'"));
                Statement::CircBuf(CircBuf::Destroy { id, layout })
            }
            "CONSTANT_DOUBLE" => {
                // CONSTANT_DOUBLE(name) = value;
                let var_name = match self.advance() {
                    Token::Ident(s) => s,
                    other => panic!("Expected variable name in CONSTANT_DOUBLE, got {other:?}"),
                };
                self.expect(&Token::RParen);
                self.expect_op("=");
                let init = self.parse_expr();
                self.consume_semicolon();
                Statement::VarDecl {
                    var_type: VarType::Real,
                    name: var_name,
                    init: Some(init),
                }
            }
            "CONSTANT_INTEGER" => {
                // CONSTANT_INTEGER(name) = value;
                let var_name = match self.advance() {
                    Token::Ident(s) => s,
                    other => panic!("Expected variable name in CONSTANT_INTEGER, got {other:?}"),
                };
                self.expect(&Token::RParen);
                self.expect_op("=");
                let init = self.parse_expr();
                self.consume_semicolon();
                Statement::VarDecl {
                    var_type: VarType::Integer,
                    name: var_name,
                    init: Some(init),
                }
            }
            "INIT_HILBERT_VARIABLES" | "DO_HILBERT_ODD" | "DO_HILBERT_EVEN" | "DO_PRICE_WMA" => {
                // Function-like macro call as statement
                let args = self.parse_call_args();
                self.expect(&Token::RParen);
                self.consume_semicolon();
                Statement::Expr(Expr::FuncCall(macro_name, args))
            }
            "HILBERT_VARIABLES" => {
                // HILBERT_VARIABLES(prefix); -> declares multiple variables
                let prefix = match self.advance() {
                    Token::Ident(s) => s,
                    other => panic!("Expected identifier in HILBERT_VARIABLES, got {other:?}"),
                };
                self.expect(&Token::RParen);
                self.consume_semicolon();
                // Expand to variable declarations for the Hilbert Transform variables
                Statement::Block {
                    body: vec![
                        Statement::VarDecl {
                            var_type: VarType::Real,
                            name: format!("{prefix}_Odd0"),
                            init: None,
                        },
                        Statement::VarDecl {
                            var_type: VarType::Real,
                            name: format!("{prefix}_Odd1"),
                            init: None,
                        },
                        Statement::VarDecl {
                            var_type: VarType::Real,
                            name: format!("{prefix}_Odd2"),
                            init: None,
                        },
                        Statement::VarDecl {
                            var_type: VarType::Real,
                            name: format!("{prefix}_Even0"),
                            init: None,
                        },
                        Statement::VarDecl {
                            var_type: VarType::Real,
                            name: format!("{prefix}_Even1"),
                            init: None,
                        },
                        Statement::VarDecl {
                            var_type: VarType::Real,
                            name: format!("{prefix}_Even2"),
                            init: None,
                        },
                    ],
                }
            }
            _ => panic!("Unhandled macro: {macro_name}"),
        }
    }

    fn type_from_keyword(s: &str) -> VarType {
        match s {
            "int" => VarType::Integer,
            "double" => VarType::Real,
            "size_t" => VarType::Index,
            "TA_RetCode" => VarType::RetCodeType,
            other => panic!("Unknown type keyword: {other}"),
        }
    }

    /// Check whether a variable name declared as `int` should be refined to
    /// `VarType::Index` (array index / loop counter, always non-negative).
    /// Variables that may hold negative values (sentinels, lookback totals,
    /// optional params) stay as `VarType::Integer`.
    fn is_index_var_name(name: &str) -> bool {
        // Common loop counters
        if matches!(name, "i" | "j" | "k") {
            return true;
        }
        // Well-known index variables
        if matches!(
            name,
            "outIdx" | "today" | "todayIdx" | "trailingIdx" | "inIdx" | "totIdx" | "idx"
        ) {
            return true;
        }
        // Variables ending in TrailingIdx (e.g. BodyLongTrailingIdx)
        if name.ends_with("TrailingIdx") {
            return true;
        }
        false
    }

    fn parse_var_decl(&mut self) -> Vec<Statement> {
        let type_tok = self.advance();
        let var_type = match type_tok {
            Token::Ident(ref s) => Self::type_from_keyword(s),
            _ => panic!("Expected type keyword"),
        };

        // Consume optional pointer declarator: `double *buf` or `int *ptr`
        let var_type = if self.peek() == Some(&Token::Star) {
            self.advance();
            match var_type {
                VarType::Real => VarType::RealPointer,
                VarType::Integer => VarType::IntPointer,
                other => other, // keep as-is for Index, RetCodeType, etc.
            }
        } else {
            var_type
        };

        let first = self.parse_single_var_decl(var_type.clone());

        // Check for multi-variable declarations: int x, y, z;
        if self.peek() == Some(&Token::Comma) {
            let mut stmts = vec![first];
            while self.peek() == Some(&Token::Comma) {
                self.advance(); // consume ,
                stmts.push(self.parse_single_var_decl(var_type.clone()));
            }
            self.consume_semicolon();
            stmts
        } else {
            self.consume_semicolon();
            vec![first]
        }
    }

    fn parse_single_var_decl(&mut self, var_type: VarType) -> Statement {
        let name = match self.advance() {
            Token::Ident(s) => s,
            other => panic!("Expected identifier in var decl, got {other:?}"),
        };

        // Refine int → Index for variables that are array indices / loop counters
        let var_type = if var_type == VarType::Integer && Self::is_index_var_name(&name) {
            VarType::Index
        } else {
            var_type
        };

        // Detect fixed-size array declarations: `double buf[SIZE]`
        let var_type = if self.peek() == Some(&Token::LBracket) {
            self.advance(); // consume [
            // Collect tokens between [ and ] as the size string
            let mut size_tokens = Vec::new();
            let mut depth = 1;
            while depth > 0 {
                let tok = self.advance();
                match &tok {
                    Token::LBracket => depth += 1,
                    Token::RBracket => depth -= 1,
                    _ => {}
                }
                if depth > 0 {
                    size_tokens.push(tok);
                }
            }
            let size_str = size_tokens
                .iter()
                .map(|t| match t {
                    Token::Ident(s) => s.clone(),
                    Token::Number(n) => n.to_string(),
                    Token::IntNumber(n) => n.to_string(),
                    Token::Op(op) => op.clone(),
                    Token::LParen => "(".to_string(),
                    Token::RParen => ")".to_string(),
                    Token::Star => "*".to_string(),
                    Token::PlusPlus => "++".to_string(),
                    Token::MinusMinus => "--".to_string(),
                    other => format!("{other:?}"),
                })
                .collect::<String>();
            match var_type {
                VarType::Real => VarType::RealArray(size_str),
                VarType::Integer => VarType::IntArray(size_str),
                other => other,
            }
        } else {
            var_type
        };

        // Check for init
        if let Some(Token::Op(ref op)) = self.peek() {
            if op == "=" {
                self.advance(); // consume =
                let init = self.parse_expr();
                return Statement::VarDecl {
                    var_type,
                    name,
                    init: Some(init),
                };
            }
        }

        Statement::VarDecl {
            var_type,
            name,
            init: None,
        }
    }

    /// Parse the right-hand side of an assignment, handling chained assignments.
    /// Returns (`preceding_assignments`, `final_value`) where `preceding_assignments`
    /// are the desugared inner assignments. Does NOT consume the trailing semicolon.
    /// Example: for `b = c = 0.0`, returns ([c = 0.0, b = c], Var(c))
    ///          for `expr`, returns ([], expr)
    fn parse_chained_rhs(&mut self) -> (Vec<Statement>, Expr) {
        // Look ahead: if next is Ident followed by Op("="), it's a chain.
        if let Some(Token::Ident(_)) = self.peek() {
            let save = self.pos;
            let chain_name = if let Token::Ident(s) = &self.tokens[self.pos] {
                s.clone()
            } else {
                unreachable!()
            };
            self.advance();
            if matches!(self.peek(), Some(Token::Op(ref o)) if o == "=") {
                self.advance(); // consume =
                let chain_name = strip_ta_prefix(&chain_name);
                // Recursively parse the rest of the chain
                let (mut inner_stmts, inner_val) = self.parse_chained_rhs();
                inner_stmts.push(Statement::Assign {
                    target: Expr::Var(chain_name.clone()),
                    value: inner_val,
                    compound: false,
                });
                return (inner_stmts, Expr::Var(chain_name));
            }
            // Not a chain — backtrack
            self.pos = save;
        }
        // Plain expression
        let expr = self.parse_expr();
        (vec![], expr)
    }

    fn parse_pointer_deref_assign(&mut self) -> Statement {
        self.advance(); // consume *
        let name = match self.advance() {
            Token::Ident(s) => s,
            other => panic!("Expected identifier after *, got {other:?}"),
        };
        self.expect_op("=");
        // Check for chained assignment: *ptr = name2 = expr
        let (chained_stmts, value) = self.parse_chained_rhs();
        self.consume_semicolon();
        let mut stmts = chained_stmts;
        stmts.push(Statement::Assign {
            target: Expr::PointerDeref(name),
            value,
            compound: false,
        });
        if stmts.len() == 1 {
            stmts.remove(0)
        } else {
            Statement::Block { body: stmts }
        }
    }

    fn parse_do_while(&mut self) -> Statement {
        self.advance(); // consume "do"
        self.expect(&Token::LBrace);
        let body = self.parse_statements();
        self.expect(&Token::RBrace);
        // expect "while"
        match self.advance() {
            Token::Ident(s) if s == "while" => {}
            other => panic!("Expected 'while' after do block, got {other:?}"),
        }
        self.expect(&Token::LParen);
        let condition = self.parse_expr();
        self.expect(&Token::RParen);
        self.consume_semicolon();
        Statement::DoWhile { condition, body }
    }

    fn parse_while(&mut self) -> Statement {
        self.advance(); // consume "while"
        self.expect(&Token::LParen);
        let condition = self.parse_expr();
        self.expect(&Token::RParen);
        let body = if self.peek() == Some(&Token::LBrace) {
            self.advance(); // consume {
            let body = self.parse_statements();
            self.expect(&Token::RBrace);
            body
        } else {
            // Braceless while: single statement
            let stmt = self.parse_statement();
            vec![stmt]
        };
        Statement::While { condition, body }
    }

    fn parse_for(&mut self) -> Statement {
        self.advance(); // consume "for"
        self.expect(&Token::LParen);

        // Parse init statement(s) — may have comma-separated assignments
        // e.g. todayIdx=startIdx, outIdx=0
        let mut init_stmts = vec![self.parse_for_init()];
        while self.peek() == Some(&Token::Comma) {
            self.advance(); // consume comma
            init_stmts.push(self.parse_for_init());
        }
        self.expect(&Token::Semicolon);

        // Use compound init if multiple, or single if just one
        let init = if init_stmts.len() == 1 {
            init_stmts.remove(0)
        } else {
            Statement::Block { body: init_stmts }
        };

        // Parse condition
        let condition = self.parse_expr();
        self.expect(&Token::Semicolon);

        // Parse update statement(s) — may have comma-separated updates
        // or may be empty (e.g., for(;;))
        let update = if self.peek() == Some(&Token::RParen) {
            // Empty update
            Statement::Block { body: vec![] }
        } else {
            let mut update_stmts = vec![self.parse_for_update()];
            while self.peek() == Some(&Token::Comma) {
                self.advance(); // consume comma
                update_stmts.push(self.parse_for_update());
            }
            if update_stmts.len() == 1 {
                update_stmts.remove(0)
            } else {
                Statement::Block { body: update_stmts }
            }
        };
        self.expect(&Token::RParen);

        // Parse body — may or may not have braces
        let body = if self.peek() == Some(&Token::LBrace) {
            self.advance(); // consume {
            let stmts = self.parse_statements();
            self.expect(&Token::RBrace);
            stmts
        } else {
            // Braceless for: single statement body
            vec![self.parse_statement()]
        };

        Statement::ForC {
            init: Box::new(init),
            condition,
            update: Box::new(update),
            body,
        }
    }

    fn parse_for_init(&mut self) -> Statement {
        // Could be a type keyword (var decl) or an ident (assignment)
        if let Some(Token::Ident(ref s)) = self.peek() {
            if Self::is_type_keyword(s) {
                let type_tok = self.advance();
                let var_type = match type_tok {
                    Token::Ident(ref s) => Self::type_from_keyword(s),
                    _ => unreachable!(),
                };
                return self.parse_single_var_decl(var_type);
            }
        }
        self.parse_assignment_no_semicolon()
    }

    fn parse_for_update(&mut self) -> Statement {
        // Handle pre-increment: ++i
        if self.peek() == Some(&Token::PlusPlus) {
            self.advance();
            let name = match self.advance() {
                Token::Ident(s) => s,
                other => panic!("Expected identifier after ++ in for update, got {other:?}"),
            };
            return inc_dec_assign(name, BinOp::Add);
        }
        // Handle pre-decrement: --i
        if self.peek() == Some(&Token::MinusMinus) {
            self.advance();
            let name = match self.advance() {
                Token::Ident(s) => s,
                other => panic!("Expected identifier after -- in for update, got {other:?}"),
            };
            return inc_dec_assign(name, BinOp::Sub);
        }
        // Usually i++ or i-- or i += 1
        let name = match self.advance() {
            Token::Ident(s) => s,
            other => panic!("Expected identifier in for update, got {other:?}"),
        };

        match self.peek() {
            Some(Token::PlusPlus) => {
                self.advance();
                inc_dec_assign(name, BinOp::Add)
            }
            Some(Token::MinusMinus) => {
                self.advance();
                inc_dec_assign(name, BinOp::Sub)
            }
            Some(Token::Op(ref op)) if is_compound_assign_op(op) => {
                let op_str = op.clone();
                self.advance();
                let rhs = self.parse_expr();
                compound_assign(Expr::Var(name), &op_str, rhs)
            }
            _ => {
                // Simple assignment: name = expr
                self.expect_op("=");
                let value = self.parse_expr();
                Statement::Assign {
                    target: Expr::Var(name),
                    value,
                    compound: false,
                }
            }
        }
    }

    fn parse_if(&mut self) -> Statement {
        self.advance(); // consume "if"
        self.expect(&Token::LParen);
        // For a pure top-level `&&`-chain condition, capture each operand's
        // trailing comment so backends can render it inline (CDL patterns). If the
        // condition has a top-level `||` or ternary, fall back to a normal parse
        // (comments there would clump after the if — a rare, accepted case).
        let (condition, cond_comments) = if self.condition_has_top_level_or_ternary() {
            (self.parse_expr(), Vec::new())
        } else {
            self.parse_and_chain_collecting()
        };
        self.expect(&Token::RParen);

        let then_body = if self.peek() == Some(&Token::LBrace) {
            self.advance(); // consume {
            let body = self.parse_statements();
            self.expect(&Token::RBrace);
            body
        } else {
            // Braceless if: single statement
            let stmt = self.parse_statement();
            vec![stmt]
        };

        let else_body = if let Some(Token::Ident(ref s)) = self.peek() {
            if s == "else" {
                self.advance(); // consume "else"
                if let Some(Token::Ident(ref s2)) = self.peek() {
                    if s2 == "if" {
                        // else if — recurse
                        vec![self.parse_if()]
                    } else {
                        // else { ... } or braceless else
                        if self.peek() == Some(&Token::LBrace) {
                            self.advance();
                            let body = self.parse_statements();
                            self.expect(&Token::RBrace);
                            body
                        } else {
                            vec![self.parse_statement()]
                        }
                    }
                } else if self.peek() == Some(&Token::LBrace) {
                    self.advance();
                    let body = self.parse_statements();
                    self.expect(&Token::RBrace);
                    body
                } else {
                    vec![self.parse_statement()]
                }
            } else {
                vec![]
            }
        } else {
            vec![]
        };

        Statement::If {
            condition,
            then_body,
            else_body,
            cond_comments,
        }
    }

    /// Scan the (already-opened) if-condition for a top-level `||` or ternary `?`
    /// at paren-depth 0, stopping at the closing `)`. Such conditions are parsed
    /// normally (no inline operand-comment collection).
    fn condition_has_top_level_or_ternary(&self) -> bool {
        let mut depth = 0i32;
        let mut i = self.pos;
        while i < self.tokens.len() {
            match &self.tokens[i] {
                Token::LParen => depth += 1,
                Token::RParen => {
                    if depth == 0 {
                        return false;
                    }
                    depth -= 1;
                }
                Token::Op(op) if depth == 0 && op == "||" => return true,
                Token::Question if depth == 0 => return true,
                _ => {}
            }
            i += 1;
        }
        false
    }

    /// Parse a top-level `&&`-chain condition, collecting each operand's trailing
    /// comment (the comment that follows it before the next `&&` / closing `)`).
    /// Returns the rebuilt left-assoc `&&` expression and one comment slot per
    /// operand (in order). If no operand carried a comment, the returned vec is
    /// empty (so backends render the flat one-liner as before).
    fn parse_and_chain_collecting(&mut self) -> (Expr, Vec<Option<Vec<String>>>) {
        let mut operands = Vec::new();
        let mut op_comments: Vec<Vec<String>> = Vec::new();
        // Own-line comments seen after one operand's `&&` annotate the *next*
        // operand; carried forward here.
        let mut pending_leading: Vec<String> = Vec::new();
        loop {
            operands.push(self.parse_bitwise_or());
            op_comments.push(std::mem::take(&mut pending_leading));
            let idx = operands.len() - 1;
            if matches!(self.peek(), Some(Token::Op(op)) if op == "&&") {
                self.advance(); // consume &&
                // Comments between this operand's `&&` and the next operand:
                // same-line (trailing) → this operand; own-line → the next one.
                while self.comment_idx < self.comments.len()
                    && self.comments[self.comment_idx].0 <= self.pos
                {
                    let (_, lines, trailing) = self.comments[self.comment_idx].clone();
                    self.comment_idx += 1;
                    if trailing {
                        op_comments[idx].extend(lines);
                    } else {
                        pending_leading.extend(lines);
                    }
                }
            } else {
                // Last operand: attach any remaining comment before the `)`.
                while self.comment_idx < self.comments.len()
                    && self.comments[self.comment_idx].0 <= self.pos
                {
                    let lines = self.comments[self.comment_idx].1.clone();
                    self.comment_idx += 1;
                    op_comments[idx].extend(lines);
                }
                break;
            }
        }

        let comments: Vec<Option<Vec<String>>> = op_comments
            .into_iter()
            .map(|c| if c.is_empty() { None } else { Some(c) })
            .collect();
        let mut expr = operands.remove(0);
        for op in operands {
            expr = Expr::BinOp(Box::new(expr), BinOp::And, Box::new(op));
        }
        if comments.iter().any(Option::is_some) {
            (expr, comments)
        } else {
            (expr, Vec::new())
        }
    }

    fn parse_switch(&mut self) -> Statement {
        self.advance(); // consume "switch"
        self.expect(&Token::LParen);
        let expr = self.parse_expr();
        self.expect(&Token::RParen);
        self.expect(&Token::LBrace);

        let mut cases: Vec<(String, Vec<Statement>)> = Vec::new();
        let mut default_body: Vec<Statement> = Vec::new();

        while self.peek() != Some(&Token::RBrace) {
            match self.peek().cloned() {
                Some(Token::Ident(ref s)) if s == "case" => {
                    self.advance();
                    let label = match self.advance() {
                        Token::Ident(s) => {
                            // Handle ENUM_CASE(Type, CName, PascalName) macro:
                            // Produce label in "Type_Short" format, e.g. "MAType_SMA"
                            // so lookup_variant can find it in the enum registry.
                            if s == "ENUM_CASE" {
                                self.expect(&Token::LParen);
                                let enum_type = match self.advance() {
                                    Token::Ident(n) => n,
                                    other => {
                                        panic!("Expected ENUM_CASE type name, got {other:?}")
                                    }
                                };
                                self.expect(&Token::Comma);
                                let c_name = match self.advance() {
                                    Token::Ident(n) => n,
                                    other => panic!("Expected ENUM_CASE C name, got {other:?}"),
                                };
                                self.expect(&Token::Comma);
                                let _pascal_name = self.advance(); // e.g. Sma
                                self.expect(&Token::RParen);
                                // Strip "TA_<Type>_" prefix from c_name to get short name
                                let prefix = format!("TA_{enum_type}_");
                                let short = c_name.strip_prefix(&prefix).unwrap_or(&c_name);
                                format!("{enum_type}_{short}")
                            } else {
                                // Strip TA_ prefix from raw enum labels like
                                // TA_MAType_SMA → MAType_SMA so lookup_variant
                                // can match them against the enum registry.
                                s.strip_prefix("TA_").unwrap_or(&s).to_string()
                            }
                        }
                        Token::IntNumber(n) => format!("{n}"),
                        other => panic!("Expected case label, got {other:?}"),
                    };
                    self.expect(&Token::Colon);

                    let mut body = Vec::new();
                    loop {
                        // Flush comments at statement boundaries (the case body is
                        // parsed here directly, not via parse_statements).
                        self.flush_comments(self.pos, &mut body);
                        match self.peek() {
                            Some(Token::RBrace) => break,
                            Some(Token::Ident(ref s)) if s == "case" || s == "default" => {
                                break;
                            }
                            Some(Token::Ident(ref s)) if s == "break" => {
                                self.advance();
                                self.consume_semicolon();
                                break;
                            }
                            _ => body.push(self.parse_statement()),
                        }
                    }
                    cases.push((label, body));
                }
                Some(Token::Ident(ref s)) if s == "default" => {
                    self.advance();
                    self.expect(&Token::Colon);
                    loop {
                        self.flush_comments(self.pos, &mut default_body);
                        match self.peek() {
                            Some(Token::RBrace) => break,
                            Some(Token::Ident(ref s)) if s == "break" => {
                                self.advance();
                                self.consume_semicolon();
                                break;
                            }
                            _ => default_body.push(self.parse_statement()),
                        }
                    }
                }
                other => panic!("Expected 'case' or 'default' in switch, got {other:?}"),
            }
        }
        self.expect(&Token::RBrace);

        Statement::Switch {
            expr,
            cases,
            default: default_body,
        }
    }

    fn parse_return(&mut self) -> Statement {
        self.advance(); // consume "return"

        // Check for bare return (just semicolon)
        if self.peek() == Some(&Token::Semicolon) {
            self.consume_semicolon();
            return Statement::Return { value: None };
        }

        // Parse the return expression, but first handle TA_ prefix stripping
        // for identifiers that look like enum values (e.g., TA_SUCCESS -> SUCCESS)
        let expr = self.parse_return_expr();
        self.consume_semicolon();

        Statement::Return { value: Some(expr) }
    }

    /// Parse a return value expression, stripping TA_ prefixes from identifiers.
    fn parse_return_expr(&mut self) -> Expr {
        // Use the normal expression parser, but we need to handle TA_ prefix stripping.
        // We'll do this by peeking at the tokens and transforming TA_ prefixed idents.
        // The simplest approach: rewrite TA_ prefixed idents in-place. This rewrite is
        // permanent — the token stream up to the next semicolon is mutated before parsing.
        let start = self.pos;

        // Scan forward to find the semicolon, stripping TA_ prefixes
        let mut end = self.pos;
        while end < self.tokens.len() && self.tokens[end] != Token::Semicolon {
            end += 1;
        }
        // Strip TA_ prefixes in the range [start..end]
        for i in start..end {
            if let Token::Ident(ref s) = self.tokens[i].clone() {
                let stripped = strip_ta_prefix(s);
                if stripped != *s {
                    self.tokens[i] = Token::Ident(stripped);
                }
            }
        }

        self.parse_expr()
    }

    /// Check if an identifier looks like an `ALL_CAPS` macro name.
    fn is_all_caps_macro(name: &str) -> bool {
        name.len() > 1
            && name
                .chars()
                .all(|c| c.is_ascii_uppercase() || c == '_' || c.is_ascii_digit())
            && name.chars().next().is_some_and(|c| c.is_ascii_uppercase())
    }

    /// Parse a statement starting with `(`, which in TA-Lib source is always
    /// `(void)identifier;` — a C idiom to suppress unused-variable warnings.
    /// Treated as a no-op (discarded block).
    fn parse_void_cast_stmt(&mut self) -> Statement {
        self.advance(); // consume (
        // Expect a type keyword (void, int, double, etc.) or identifier
        let _type_name = match self.advance() {
            Token::Ident(s) => s,
            other => panic!("Expected type identifier after '(', got {other:?}"),
        };
        self.expect(&Token::RParen); // consume )
        // Consume the target expression until semicolon
        while self.peek() != Some(&Token::Semicolon) && self.pos < self.tokens.len() {
            self.advance();
        }
        self.consume_semicolon();
        // Emit as an empty block (no-op)
        Statement::Block { body: vec![] }
    }

    #[allow(clippy::too_many_lines)]
    fn parse_assignment_or_expr_stmt(&mut self) -> Statement {
        let name = match self.advance() {
            Token::Ident(s) => s,
            other => panic!("Expected identifier, got {other:?}"),
        };

        // Handle ALL_CAPS macro as standalone statement without args: CALCULATE_AD;
        if Self::is_all_caps_macro(&name) && self.peek() == Some(&Token::Semicolon) {
            self.consume_semicolon();
            return Statement::Expr(Expr::FuncCall(name, vec![]));
        }

        // Handle ALL_CAPS macro calls as standalone statements
        // e.g., TRUE_RANGE(a,b,c,d); UNUSED_VARIABLE(x); SAR_ROUNDING(v);
        if Self::is_all_caps_macro(&name)
            && self.peek() == Some(&Token::LParen)
            && !Self::is_type_keyword(&name)
        // not a type keyword
        {
            let save_pos = self.pos;
            self.advance(); // consume (
            let args = self.parse_call_args();
            self.expect(&Token::RParen);
            if matches!(self.peek(), Some(&Token::Semicolon) | None)
                || self.pos >= self.tokens.len()
            {
                self.consume_semicolon();
                return Statement::Expr(Expr::FuncCall(name, args));
            }
            // Not a standalone call — might be CONSTANT_DOUBLE(x) = expr or similar
            self.pos = save_pos;
        }

        // Post-increment: i++;
        if self.peek() == Some(&Token::PlusPlus) {
            self.advance();
            self.consume_semicolon();
            return inc_dec_assign(name, BinOp::Add);
        }

        // Post-decrement: i--;
        if self.peek() == Some(&Token::MinusMinus) {
            self.advance();
            self.consume_semicolon();
            return inc_dec_assign(name, BinOp::Sub);
        }

        // Function call as statement: FUNC(args...);
        if self.peek() == Some(&Token::LParen) {
            let save_pos = self.pos;
            self.advance(); // consume (
            let args = self.parse_call_args();
            self.expect(&Token::RParen);

            if matches!(self.peek(), Some(&Token::Semicolon) | None)
                || self.pos >= self.tokens.len()
            {
                self.consume_semicolon();
                let func_name = transform_func_name(&name);
                return Statement::Expr(Expr::FuncCall(func_name, args));
            }

            // CIRCBUF_REF(arr[idx])field = expr; or compound assign
            if name == "CIRCBUF_REF" {
                if let Some(Token::Ident(field)) = self.peek().cloned() {
                    self.advance(); // consume field name
                                    // Build synthetic array name: arr_field
                    let target =
                        if let Some(Expr::ArrayAccess(arr_name, idx)) = args.into_iter().next() {
                            Expr::ArrayAccess(format!("{arr_name}_{field}"), idx)
                        } else {
                            Expr::Var(format!("circbuf_{field}"))
                        };
                    // Parse = or compound assignment
                    match self.peek().cloned() {
                        Some(Token::Op(ref op)) if op == "=" => {
                            self.advance();
                            let value = self.parse_expr();
                            self.consume_semicolon();
                            return Statement::Assign {
                                target,
                                value,
                                compound: false,
                            };
                        }
                        Some(Token::Op(ref op)) if is_compound_assign_op(op) => {
                            let op_str = op.clone();
                            self.advance();
                            let rhs = self.parse_expr();
                            self.consume_semicolon();
                            return compound_assign(target, &op_str, rhs);
                        }
                        _ => {} // fall through
                    }
                }
            }

            // Not a standalone call — backtrack
            self.pos = save_pos;
        }

        // Array access assignment: arr[i] = expr;
        if self.peek() == Some(&Token::LBracket) {
            self.advance(); // consume [
            let index_expr = self.parse_expr();
            self.expect(&Token::RBracket);

            // Handle struct member access: arr[idx].field = expr;
            // Flatten to arr_field[idx] (same pattern as CIRCBUF_REF)
            if self.peek() == Some(&Token::Dot) {
                self.advance(); // consume .
                let field = match self.advance() {
                    Token::Ident(s) => s,
                    other => panic!("Expected field name after '.', got {other:?}"),
                };
                let flat_name = format!("{name}_{field}");
                match self.peek().cloned() {
                    Some(Token::Op(ref op)) if is_compound_assign_op(op) => {
                        let op_str = op.clone();
                        self.advance();
                        let rhs = self.parse_expr();
                        self.consume_semicolon();
                        let target = Expr::ArrayAccess(flat_name, Box::new(index_expr));
                        return compound_assign(target, &op_str, rhs);
                    }
                    _ => {
                        self.expect_op("=");
                        let value = self.parse_expr();
                        self.consume_semicolon();
                        return Statement::Assign {
                            target: Expr::ArrayAccess(flat_name, Box::new(index_expr)),
                            value,
                            compound: false,
                        };
                    }
                }
            }

            // Check for compound assignment on array
            match self.peek().cloned() {
                Some(Token::Op(ref op)) if is_compound_assign_op(op) => {
                    let op_str = op.clone();
                    self.advance();
                    let rhs = self.parse_expr();
                    self.consume_semicolon();
                    let target = Expr::ArrayAccess(name, Box::new(index_expr));
                    return compound_assign(target, &op_str, rhs);
                }
                _ => {
                    self.expect_op("=");
                    let value = self.parse_expr();
                    self.consume_semicolon();
                    return Statement::Assign {
                        target: Expr::ArrayAccess(name, Box::new(index_expr)),
                        value,
                        compound: false,
                    };
                }
            }
        }

        // Simple or compound assignment
        match self.peek().cloned() {
            Some(Token::Op(ref op)) if op == "=" => {
                self.advance();
                let (mut chain_stmts, value) = self.parse_chained_rhs();
                self.consume_semicolon();
                chain_stmts.push(Statement::Assign {
                    target: Expr::Var(name),
                    value,
                    compound: false,
                });
                if chain_stmts.len() == 1 {
                    chain_stmts.remove(0)
                } else {
                    Statement::Block { body: chain_stmts }
                }
            }
            Some(Token::Op(ref op)) if is_compound_assign_op(op) => {
                let op_str = op.clone();
                self.advance();
                // Handle embedded assignment: SumY += tempValue1 = inReal[i]
                let (mut chain_stmts, rhs) = self.parse_chained_rhs();
                self.consume_semicolon();
                chain_stmts.push(compound_assign(Expr::Var(name), &op_str, rhs));
                if chain_stmts.len() == 1 {
                    chain_stmts.remove(0)
                } else {
                    Statement::Block { body: chain_stmts }
                }
            }
            // Custom type variable declaration: `TypeName varname[size];` or `TypeName varname;`
            // (e.g., `MoneyFlow mflow[50];`) — treat as no-op since we flatten struct accesses.
            Some(Token::Ident(_)) => {
                while self.peek() != Some(&Token::Semicolon) && self.pos < self.tokens.len() {
                    self.advance();
                }
                self.consume_semicolon();
                Statement::Block { body: vec![] }
            }
            other => panic!("Expected '=' or compound assignment after '{name}', got {other:?}"),
        }
    }

    fn parse_assignment_no_semicolon(&mut self) -> Statement {
        let name = match self.advance() {
            Token::Ident(s) => s,
            other => panic!("Expected identifier, got {other:?}"),
        };
        self.expect_op("=");
        let value = self.parse_expr();
        Statement::Assign {
            target: Expr::Var(name),
            value,
            compound: false,
        }
    }

    fn parse_call_args(&mut self) -> Vec<Expr> {
        let mut args = Vec::new();
        if self.peek() == Some(&Token::RParen) {
            return args;
        }
        args.push(self.parse_expr());
        while self.peek() == Some(&Token::Comma) {
            self.advance();
            args.push(self.parse_expr());
        }
        args
    }

    // --- Expression parsing (precedence climbing) ---

    fn parse_expr(&mut self) -> Expr {
        let expr = self.parse_logical_or();
        // Ternary: expr ? then_expr : else_expr
        if self.peek() == Some(&Token::Question) {
            self.advance();
            let then_expr = self.parse_expr();
            self.expect(&Token::Colon);
            let else_expr = self.parse_expr();
            return Expr::Ternary(Box::new(expr), Box::new(then_expr), Box::new(else_expr));
        }
        expr
    }

    fn parse_logical_or(&mut self) -> Expr {
        let mut left = self.parse_logical_and();
        while let Some(Token::Op(ref op)) = self.peek() {
            if op != "||" {
                break;
            }
            self.advance();
            let right = self.parse_logical_and();
            left = Expr::BinOp(Box::new(left), BinOp::Or, Box::new(right));
        }
        left
    }

    fn parse_logical_and(&mut self) -> Expr {
        let mut left = self.parse_bitwise_or();
        while let Some(Token::Op(ref op)) = self.peek() {
            if op != "&&" {
                break;
            }
            self.advance();
            let right = self.parse_bitwise_or();
            left = Expr::BinOp(Box::new(left), BinOp::And, Box::new(right));
        }
        left
    }

    fn parse_bitwise_or(&mut self) -> Expr {
        let mut left = self.parse_comparison();
        while self.peek() == Some(&Token::Pipe) {
            self.advance();
            let right = self.parse_comparison();
            left = Expr::BinOp(Box::new(left), BinOp::BitwiseOr, Box::new(right));
        }
        left
    }

    fn parse_comparison(&mut self) -> Expr {
        let mut left = self.parse_shift();
        while let Some(Token::Op(ref op)) = self.peek() {
            let bin_op = match op.as_str() {
                "<=" => BinOp::LessEq,
                ">=" => BinOp::GreaterEq,
                "<" => BinOp::Less,
                ">" => BinOp::Greater,
                "==" => BinOp::Eq,
                "!=" => BinOp::NotEq,
                _ => break,
            };
            self.advance();
            let right = self.parse_shift();
            left = Expr::BinOp(Box::new(left), bin_op, Box::new(right));
        }
        left
    }

    fn parse_shift(&mut self) -> Expr {
        let mut left = self.parse_additive();
        while let Some(Token::Op(ref op)) = self.peek() {
            let bin_op = match op.as_str() {
                ">>" => BinOp::Shr,
                "<<" => BinOp::Shl,
                _ => break,
            };
            self.advance();
            let right = self.parse_additive();
            left = Expr::BinOp(Box::new(left), bin_op, Box::new(right));
        }
        left
    }

    fn parse_additive(&mut self) -> Expr {
        let mut left = self.parse_multiplicative();
        while let Some(Token::Op(ref op)) = self.peek() {
            let bin_op = match op.as_str() {
                "+" => BinOp::Add,
                "-" => BinOp::Sub,
                _ => break,
            };
            self.advance();
            let right = self.parse_multiplicative();
            left = Expr::BinOp(Box::new(left), bin_op, Box::new(right));
        }
        left
    }

    fn parse_multiplicative(&mut self) -> Expr {
        let mut left = self.parse_unary();
        loop {
            match self.peek() {
                Some(Token::Star) => {
                    self.advance();
                    let right = self.parse_unary();
                    left = Expr::BinOp(Box::new(left), BinOp::Mul, Box::new(right));
                }
                Some(Token::Op(ref op)) if op == "/" || op == "%" => {
                    let bin_op = match op.as_str() {
                        "/" => BinOp::Div,
                        "%" => BinOp::Mod,
                        _ => unreachable!(),
                    };
                    self.advance();
                    let right = self.parse_unary();
                    left = Expr::BinOp(Box::new(left), bin_op, Box::new(right));
                }
                _ => break,
            }
        }
        left
    }

    fn parse_unary(&mut self) -> Expr {
        if self.peek() == Some(&Token::Bang) {
            self.advance();
            let operand = self.parse_unary();
            return Expr::Not(Box::new(operand));
        }
        // Address-of: &var or &var[idx]
        if self.peek() == Some(&Token::Ampersand) {
            self.advance();
            let operand = self.parse_primary();
            return Expr::AddressOf(Box::new(operand));
        }
        // Unary dereference: *var
        if self.peek() == Some(&Token::Star) {
            self.advance();
            if let Some(Token::Ident(ref name)) = self.peek() {
                let name = name.clone();
                self.advance();
                return Expr::PointerDeref(name);
            }
            // Dereference of a parenthesized expr — fall through to parse_primary
            let operand = self.parse_primary();
            if let Expr::Var(name) = operand {
                return Expr::PointerDeref(name);
            }
            panic!("Cannot dereference non-identifier expression");
        }
        // Pre-increment: ++expr
        if self.peek() == Some(&Token::PlusPlus) {
            self.advance();
            let operand = self.parse_unary();
            return Expr::PreIncrement(Box::new(operand));
        }
        // Pre-decrement: --expr
        if self.peek() == Some(&Token::MinusMinus) {
            self.advance();
            let operand = self.parse_unary();
            return Expr::PreDecrement(Box::new(operand));
        }
        // Unary minus or unary plus
        if let Some(Token::Op(ref op)) = self.peek() {
            if op == "-" {
                self.advance();
                let operand = self.parse_unary();
                return Expr::BinOp(Box::new(Expr::IntLiteral(0)), BinOp::Sub, Box::new(operand));
            }
            if op == "+" {
                self.advance();
                // Unary plus is a no-op
                return self.parse_unary();
            }
        }
        self.parse_primary()
    }

    fn parse_primary(&mut self) -> Expr {
        if let Some(Token::LParen) = self.peek().cloned() {
            self.advance(); // consume (
                            // Check for C-style cast: (double), (int), (size_t)
            if let Some(Token::Ident(ref s)) = self.peek() {
                if Self::is_type_keyword(s) {
                    let type_name = s.clone();
                    self.advance();
                    self.expect(&Token::RParen);
                    let var_type = Self::type_from_keyword(&type_name);
                    let operand = self.parse_unary();
                    return Expr::Cast(var_type, Box::new(operand));
                }
            }
            // Parenthesized expression
            let expr = self.parse_expr();
            self.expect(&Token::RParen);
            expr
        } else {
            let tok = self.advance();
            match tok {
                Token::IntNumber(n) => Expr::IntLiteral(n),
                Token::Number(n) => Expr::Literal(n),
                Token::Ident(name) => {
                    // Function call
                    if self.peek() == Some(&Token::LParen) {
                        self.advance(); // consume (
                        let args = self.parse_call_args();
                        self.expect(&Token::RParen);

                        // CIRCBUF_REF(arr[idx])field -> ArrayAccess("arr_field", idx)
                        if name == "CIRCBUF_REF" {
                            if let Some(Token::Ident(field)) = self.peek().cloned() {
                                self.advance();
                                // Extract array name and index from the first arg
                                if let Some(Expr::ArrayAccess(arr_name, idx)) =
                                    args.into_iter().next()
                                {
                                    return Expr::ArrayAccess(format!("{arr_name}_{field}"), idx);
                                }
                            }
                            // Fallback: treat as opaque function call
                            return Expr::FuncCall("CIRCBUF_REF".to_string(), vec![]);
                        }

                        let func_name = transform_func_name(&name);
                        return Expr::FuncCall(func_name, args);
                    }
                    // Array access
                    if self.peek() == Some(&Token::LBracket) {
                        self.advance(); // consume [
                        let index = self.parse_expr();
                        self.expect(&Token::RBracket);
                        // Struct member access: arr[idx].field → arr_field[idx]
                        let arr_name = if self.peek() == Some(&Token::Dot) {
                            self.advance(); // consume .
                            let field = match self.advance() {
                                Token::Ident(s) => s,
                                other => panic!("Expected field name after '.', got {other:?}"),
                            };
                            format!("{name}_{field}")
                        } else {
                            name
                        };
                        let arr_expr = Expr::ArrayAccess(arr_name, Box::new(index));
                        // Check for postfix ++/-- on array access
                        if self.peek() == Some(&Token::PlusPlus) {
                            self.advance();
                            return Expr::PostIncrement(Box::new(arr_expr));
                        }
                        if self.peek() == Some(&Token::MinusMinus) {
                            self.advance();
                            return Expr::PostDecrement(Box::new(arr_expr));
                        }
                        return arr_expr;
                    }
                    // Postfix ++/-- on plain identifier
                    if self.peek() == Some(&Token::PlusPlus) {
                        self.advance();
                        return Expr::PostIncrement(Box::new(Expr::Var(strip_ta_prefix(&name))));
                    }
                    if self.peek() == Some(&Token::MinusMinus) {
                        self.advance();
                        return Expr::PostDecrement(Box::new(Expr::Var(strip_ta_prefix(&name))));
                    }
                    // Plain identifier — strip TA_ from enum-like values
                    Expr::Var(strip_ta_prefix(&name))
                }
                other => panic!("Unexpected token in expression: {other:?}"),
            }
        }
    }
}

// --- Helper functions ---

/// Strip `TA_` prefix from enum-like identifiers (all caps after TA_).
/// Also strips `TA_COMPATIBILITY_` prefix to just the enum variant name.
fn strip_ta_prefix(name: &str) -> String {
    // Handle TA_COMPATIBILITY_XXX -> strip to just the variant (METASTOCK, DEFAULT)
    if let Some(rest) = name.strip_prefix("TA_COMPATIBILITY_") {
        if rest
            .chars()
            .all(|c| c.is_ascii_uppercase() || c == '_' || c.is_ascii_digit())
        {
            return rest.to_string();
        }
    }
    if let Some(rest) = name.strip_prefix("TA_") {
        // Only strip if the rest looks like an enum value (uppercase/underscores)
        if rest
            .chars()
            .all(|c| c.is_ascii_uppercase() || c == '_' || c.is_ascii_digit())
        {
            return rest.to_string();
        }
    }
    name.to_string()
}

/// Transform function names: strip TA_ prefix and handle special mappings.
fn transform_func_name(name: &str) -> String {
    if name == "TA_GetUnstablePeriod" {
        return "UNSTABLE_PERIOD".to_string();
    }
    if name == "TA_GetCompatibility" {
        return "COMPATIBILITY".to_string();
    }
    if let Some(rest) = name.strip_prefix("TA_") {
        return rest.to_string();
    }
    name.to_string()
}

/// Map compound assignment operator string to `BinOp`.
fn compound_op(op: &str) -> BinOp {
    match op {
        "+=" => BinOp::Add,
        "-=" => BinOp::Sub,
        "*=" => BinOp::Mul,
        "/=" => BinOp::Div,
        _ => panic!("Unknown compound operator: {op}"),
    }
}

/// True for the compound-assignment operator tokens (`+=`, `-=`, `*=`, `/=`).
fn is_compound_assign_op(op: &str) -> bool {
    matches!(op, "+=" | "-=" | "*=" | "/=")
}

/// Desugar `++name` / `name++` (and the `--` forms) into `name = name <op> 1`.
fn inc_dec_assign(name: String, op: BinOp) -> Statement {
    Statement::Assign {
        target: Expr::Var(name.clone()),
        value: Expr::BinOp(Box::new(Expr::Var(name)), op, Box::new(Expr::IntLiteral(1))),
        compound: true,
    }
}

/// Desugar a compound assignment `target <op>= rhs` into `target = target <op> rhs`.
fn compound_assign(target: Expr, op_str: &str, rhs: Expr) -> Statement {
    let bin_op = compound_op(op_str);
    Statement::Assign {
        target: target.clone(),
        value: Expr::BinOp(Box::new(target), bin_op, Box::new(rhs)),
        compound: true,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_tokenize_simple() {
        let tokens = tokenize("int x = 0;");
        assert_eq!(
            tokens,
            vec![
                Token::Ident("int".to_string()),
                Token::Ident("x".to_string()),
                Token::Op("=".to_string()),
                Token::IntNumber(0),
                Token::Semicolon,
            ]
        );
    }

    #[test]
    fn test_tokenize_pointer_deref() {
        let tokens = tokenize("*outBegIdx = startIdx;");
        assert_eq!(
            tokens,
            vec![
                Token::Star,
                Token::Ident("outBegIdx".to_string()),
                Token::Op("=".to_string()),
                Token::Ident("startIdx".to_string()),
                Token::Semicolon,
            ]
        );
    }

    #[test]
    fn test_tokenize_increment_decrement() {
        let tokens = tokenize("i++; j--;");
        assert_eq!(
            tokens,
            vec![
                Token::Ident("i".to_string()),
                Token::PlusPlus,
                Token::Semicolon,
                Token::Ident("j".to_string()),
                Token::MinusMinus,
                Token::Semicolon,
            ]
        );
    }

    #[test]
    fn test_tokenize_compound_ops() {
        let tokens = tokenize("x += 1; y -= 2; z *= 3; w /= 4;");
        assert!(tokens.contains(&Token::Op("+=".to_string())));
        assert!(tokens.contains(&Token::Op("-=".to_string())));
        assert!(tokens.contains(&Token::Op("*=".to_string())));
        assert!(tokens.contains(&Token::Op("/=".to_string())));
    }

    #[test]
    fn test_tokenize_float() {
        let tokens = tokenize("0.0 1.5 42");
        assert_eq!(
            tokens,
            vec![Token::Number(0.0), Token::Number(1.5), Token::IntNumber(42),]
        );
    }

    #[test]
    fn test_tokenize_comments() {
        let tokens = tokenize("int x; // comment\n/* block */ int y;");
        assert_eq!(
            tokens,
            vec![
                Token::Ident("int".to_string()),
                Token::Ident("x".to_string()),
                Token::Semicolon,
                Token::Ident("int".to_string()),
                Token::Ident("y".to_string()),
                Token::Semicolon,
            ]
        );
    }

    #[test]
    fn test_tokenize_with_comments_captures_anchors() {
        // Comments are captured on the side, anchored to the index of the token
        // they precede; the token stream itself stays comment-free.
        let (tokens, comments) = tokenize_with_comments("int x; /* note */ int y;");
        assert_eq!(tokens.len(), 6); // int x ; int y ;
        assert_eq!(comments.len(), 1);
        assert_eq!(comments[0].0, 3); // precedes the second `int` (token index 3)
        assert_eq!(comments[0].1, vec!["note".to_string()]);
        assert!(comments[0].2); // trailing: shares the line with the preceding `;`
    }

    #[test]
    fn test_condition_comment_trailing_vs_leading() {
        // `a` gets its trailing comment; the own-line comment annotates `b`.
        let src = "\
TA_RetCode f( int a, int b, int startIdx, int endIdx, int *outBegIdx, int *outNBElement, double outReal[] )
{
    if( a > 0 &&   // first
        // second
        b > 0 )
    {
        a = 1;
    }
}
";
        let parsed = parse_c_source_str(src);
        let body = &parsed.functions[0].body;
        let Statement::If { cond_comments, .. } = &body[0] else {
            panic!("expected If, got {:?}", body[0]);
        };
        assert_eq!(cond_comments.len(), 2);
        assert_eq!(cond_comments[0], Some(vec!["first".to_string()]));
        assert_eq!(cond_comments[1], Some(vec!["second".to_string()]));
    }

    #[test]
    fn test_carries_header_and_body_comments() {
        let src = "\
/* List of contributors:
 *  MF  Mario Fortier
 */
int foo_lookback( int optInPeriod )
{
    return optInPeriod;
}
TA_RetCode foo( int startIdx, int endIdx, const double inReal[], int optInPeriod, int *outBegIdx, int *outNBElement, double outReal[] )
{
    /* Adjust the start index. */
    int x;
    x = 1;
    /* trailing note */
}
";
        let parsed = parse_c_source_str(src);
        // File-level contributors block captured as a header comment.
        assert_eq!(parsed.header_comments.len(), 1);
        assert!(parsed.header_comments[0]
            .iter()
            .any(|l| l.contains("Mario Fortier")));
        // Body comment lands positionally before the statement it preceded.
        let body = &parsed.functions[0].body;
        assert!(matches!(&body[0], Statement::Comment(lines)
            if lines[0].contains("Adjust the start index")));
        // Trailing comment (just before the closing brace) is preserved.
        assert!(body.iter().any(|s| matches!(s, Statement::Comment(lines)
            if lines.iter().any(|l| l.contains("trailing note")))));
        // Lookback comments still parse (none here) and the lookback body is intact.
        assert!(!matches!(parsed.lookback_body.first(), Some(Statement::Comment(_))));
    }

    #[test]
    fn test_tokenize_ta_prefix() {
        let tokens = tokenize("TA_RetCode TA_MULT TA_SUCCESS");
        assert_eq!(
            tokens,
            vec![
                Token::Ident("TA_RetCode".to_string()),
                Token::Ident("TA_MULT".to_string()),
                Token::Ident("TA_SUCCESS".to_string()),
            ]
        );
    }

    #[test]
    fn test_extract_mult_functions() {
        let source = r"
int TA_MULT_Lookback(void)
{
    return 0;
}

TA_RetCode TA_MULT(int startIdx, int endIdx,
                   const double inReal0[],
                   const double inReal1[],
                   int *outBegIdx, int *outNBElement,
                   double outReal[])
{
    size_t outIdx;
    size_t i;
    outIdx = 0;
    *outNBElement = outIdx;
    *outBegIdx = startIdx;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        assert!(!parsed.lookback_body.is_empty());
        assert_eq!(parsed.functions.len(), 1);
        assert_eq!(parsed.functions[0].name, "TA_MULT");
    }

    #[test]
    fn test_extract_internal_function() {
        let source = r"
TA_RetCode TA_INT_SMA(int startIdx, int endIdx,
                      const double inReal[],
                      int optInTimePeriod,
                      int *outBegIdx, int *outNBElement,
                      double outReal[])
{
    double periodTotal = 0.0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        assert_eq!(parsed.functions.len(), 1);
        assert_eq!(parsed.functions[0].name, "TA_INT_SMA");
    }

    #[test]
    fn test_parse_pointer_deref() {
        let source = r"
TA_RetCode TA_TEST(int startIdx, int endIdx, int *outBegIdx, int *outNBElement)
{
    *outBegIdx = startIdx;
    *outNBElement = 0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        match &body[0] {
            Statement::Assign { target, .. } => match target {
                Expr::PointerDeref(name) => assert_eq!(name, "outBegIdx"),
                other => panic!("Expected PointerDeref, got {other:?}"),
            },
            other => panic!("Expected Assign, got {other:?}"),
        }
    }

    #[test]
    fn test_parse_cast() {
        let tokens = tokenize("(size_t)startIdx");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::Cast(var_type, inner) => {
                assert_eq!(var_type, VarType::Index);
                match *inner {
                    Expr::Var(name) => assert_eq!(name, "startIdx"),
                    other => panic!("Expected Var, got {other:?}"),
                }
            }
            other => panic!("Expected Cast, got {other:?}"),
        }
    }

    #[test]
    fn test_parse_while_loop() {
        let source = r"
TA_RetCode TA_TEST(void)
{
    size_t i;
    i = 0;
    while( i <= 10 ) {
        i += 1;
    }
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        // i decl, i = 0, while loop, return
        assert_eq!(body.len(), 4);
        match &body[2] {
            Statement::While { body, .. } => {
                assert_eq!(body.len(), 1);
            }
            other => panic!("Expected While, got {other:?}"),
        }
    }

    #[test]
    fn test_post_increment() {
        let tokens = tokenize("outIdx += 1;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target, compound, ..
            } => {
                assert!(compound);
                match target {
                    Expr::Var(name) => assert_eq!(name, "outIdx"),
                    other => panic!("Expected Var, got {other:?}"),
                }
            }
            other => panic!("Expected Assign, got {other:?}"),
        }

        // Test actual i++ syntax
        let tokens2 = tokenize("i++;");
        let mut parser2 = Parser::new(tokens2);
        let stmt2 = parser2.parse_statement();
        match stmt2 {
            Statement::Assign {
                target, compound, ..
            } => {
                assert!(compound);
                match target {
                    Expr::Var(name) => assert_eq!(name, "i"),
                    other => panic!("Expected Var, got {other:?}"),
                }
            }
            other => panic!("Expected Assign, got {other:?}"),
        }
    }

    #[test]
    fn test_braceless_if() {
        let tokens = tokenize("if( x < y ) x = y;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::If {
                then_body,
                else_body,
                ..
            } => {
                assert_eq!(then_body.len(), 1);
                assert!(else_body.is_empty());
            }
            other => panic!("Expected If, got {other:?}"),
        }
    }

    #[test]
    fn test_braceless_if_else() {
        let tokens = tokenize("if( x < y ) x = y; else x = z;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::If {
                then_body,
                else_body,
                ..
            } => {
                assert_eq!(then_body.len(), 1);
                assert_eq!(else_body.len(), 1);
            }
            other => panic!("Expected If, got {other:?}"),
        }
    }

    #[test]
    fn test_multi_var_decl() {
        let tokens = tokenize("size_t i, j, k;");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 3);
        for s in &stmts {
            match s {
                Statement::VarDecl { var_type, .. } => {
                    assert_eq!(*var_type, VarType::Index);
                }
                other => panic!("Expected VarDecl, got {other:?}"),
            }
        }
    }

    #[test]
    fn test_single_var_decl() {
        let tokens = tokenize("double x = 0.0;");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl {
                var_type,
                name,
                init,
            } => {
                assert_eq!(*var_type, VarType::Real);
                assert_eq!(name, "x");
                assert!(init.is_some());
            }
            other => panic!("Expected VarDecl, got {other:?}"),
        }
    }

    #[test]
    fn test_array_access_assignment() {
        let tokens = tokenize("outReal[outIdx] = inReal0[i] * inReal1[i];");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target, compound, ..
            } => {
                assert!(!compound);
                match target {
                    Expr::ArrayAccess(name, _) => assert_eq!(name, "outReal"),
                    other => panic!("Expected ArrayAccess, got {other:?}"),
                }
            }
            other => panic!("Expected Assign, got {other:?}"),
        }
    }

    #[test]
    fn test_function_call_expr() {
        let tokens = tokenize("TA_SMA_Lookback(optInTimePeriod)");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::FuncCall(name, args) => {
                assert_eq!(name, "SMA_Lookback");
                assert_eq!(args.len(), 1);
            }
            other => panic!("Expected FuncCall, got {other:?}"),
        }
    }

    #[test]
    fn test_special_function_names() {
        let tokens = tokenize("TA_GetUnstablePeriod(FuncUnstId_RSI)");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::FuncCall(name, _) => {
                assert_eq!(name, "UNSTABLE_PERIOD");
            }
            other => panic!("Expected FuncCall, got {other:?}"),
        }

        let tokens2 = tokenize("TA_GetCompatibility()");
        let mut parser2 = Parser::new(tokens2);
        let expr2 = parser2.parse_expr();
        match expr2 {
            Expr::FuncCall(name, args) => {
                assert_eq!(name, "COMPATIBILITY");
                assert!(args.is_empty());
            }
            other => panic!("Expected FuncCall, got {other:?}"),
        }
    }

    #[test]
    fn test_strip_ta_prefix() {
        assert_eq!(strip_ta_prefix("TA_SUCCESS"), "SUCCESS");
        assert_eq!(strip_ta_prefix("TA_INTERNAL_ERROR"), "INTERNAL_ERROR");
        assert_eq!(strip_ta_prefix("startIdx"), "startIdx");
        // Mixed case should NOT be stripped
        assert_eq!(
            strip_ta_prefix("TA_GetUnstablePeriod"),
            "TA_GetUnstablePeriod"
        );
    }

    #[test]
    fn test_for_c_loop() {
        let tokens = tokenize("for( i = 0; i < 10; i++ ) { x = x + 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC {
                init,
                condition: _,
                update: _,
                body,
            } => {
                match *init {
                    Statement::Assign { ref target, .. } => match target {
                        Expr::Var(name) => assert_eq!(name, "i"),
                        other => panic!("Expected Var, got {other:?}"),
                    },
                    other => panic!("Expected Assign init, got {other:?}"),
                }
                assert_eq!(body.len(), 1);
            }
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    #[test]
    fn test_full_mult_body() {
        let source = r"
int TA_MULT_Lookback(void)
{
    return 0;
}

TA_RetCode TA_MULT(int startIdx, int endIdx,
                   const double inReal0[],
                   const double inReal1[],
                   int *outBegIdx, int *outNBElement,
                   double outReal[])
{
    size_t outIdx;
    size_t i;

    outIdx = 0;
    i = (size_t)startIdx;
    while( i <= (size_t)endIdx ) {
        outReal[outIdx] = inReal0[i] * inReal1[i];
        outIdx += 1;
        i += 1;
    }

    *outNBElement = outIdx;
    *outBegIdx = startIdx;

    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);

        // Lookback body
        assert_eq!(parsed.lookback_body.len(), 1);
        match &parsed.lookback_body[0] {
            Statement::Return {
                value: Some(Expr::IntLiteral(0)),
            } => {}
            other => panic!("Expected Return with IntLiteral(0), got {other:?}"),
        }

        // Main function
        assert_eq!(parsed.functions.len(), 1);
        let func = &parsed.functions[0];
        assert_eq!(func.name, "TA_MULT");

        let body = &func.body;
        // VarDecl outIdx, VarDecl i, outIdx=0, i=(size_t)startIdx, while, *outNBElement, *outBegIdx, return
        assert_eq!(body.len(), 8);

        // Check while loop body
        match &body[4] {
            Statement::While { body, .. } => {
                assert_eq!(body.len(), 3); // arr assign, outIdx+=1, i+=1
            }
            other => panic!("Expected While, got {other:?}"),
        }

        // Check return
        match &body[7] {
            Statement::Return {
                value: Some(Expr::Var(s)),
            } if s == "SUCCESS" => {}
            other => panic!("Expected Return with Var(SUCCESS), got {other:?}"),
        }
    }

    #[test]
    fn test_switch_statement() {
        let tokens = tokenize(
            r"switch( mode ) {
                case MODE_A:
                    x = 1;
                    break;
                case MODE_B:
                    x = 2;
                    break;
                default:
                    x = 0;
                    break;
            }",
        );
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Switch { cases, default, .. } => {
                assert_eq!(cases.len(), 2);
                assert_eq!(cases[0].0, "MODE_A");
                assert_eq!(cases[1].0, "MODE_B");
                assert_eq!(default.len(), 1);
            }
            other => panic!("Expected Switch, got {other:?}"),
        }
    }

    #[test]
    fn test_logical_operators() {
        let tokens = tokenize("x > 0 && y < 10 || z == 5");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        // Should parse as (x > 0 && y < 10) || (z == 5)
        match expr {
            Expr::BinOp(_, BinOp::Or, _) => {} // top level is Or
            other => panic!("Expected Or at top level, got {other:?}"),
        }
    }

    #[test]
    fn test_not_expression() {
        let tokens = tokenize("!flag");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::Not(inner) => match *inner {
                Expr::Var(name) => assert_eq!(name, "flag"),
                other => panic!("Expected Var, got {other:?}"),
            },
            other => panic!("Expected Not, got {other:?}"),
        }
    }

    #[test]
    fn test_extract_prefix_free_functions() {
        let source = r"
int sma_lookback(int optInTimePeriod)
{
    return optInTimePeriod - 1;
}

TA_RetCode sma(int startIdx, int endIdx,
                       const double inReal[],
                       int optInTimePeriod,
                       int *outBegIdx, int *outNBElement,
                       double outReal[])
{
    double periodTotal = 0.0;
    *outBegIdx = startIdx;
    *outNBElement = 0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        assert!(
            !parsed.lookback_body.is_empty(),
            "lookback body should be parsed from sma_lookback"
        );
        assert_eq!(parsed.functions.len(), 1);
        assert_eq!(parsed.functions[0].name, "sma");
    }

    #[test]
    fn test_vardecl_dedup_converts_redecl_to_assign() {
        let source = r"
TA_RetCode test_func(int startIdx, int endIdx, int *outBegIdx, int *outNBElement)
{
    double *buf;
    double *buf = malloc(10 * sizeof(double));
    *outBegIdx = startIdx;
    *outNBElement = 0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        // Should have 1 VarDecl (the first bare declaration) + Assign + other stmts
        let var_decls: Vec<_> = body
            .iter()
            .filter(|s| matches!(s, Statement::VarDecl { name, .. } if name == "buf"))
            .collect();
        let assigns: Vec<_> = body
            .iter()
            .filter(|s| {
                matches!(s, Statement::Assign { target: Expr::Var(n), .. } if n == "buf")
            })
            .collect();
        assert_eq!(var_decls.len(), 1, "Expected exactly 1 VarDecl for 'buf', got {}", var_decls.len());
        assert_eq!(assigns.len(), 1, "Expected exactly 1 Assign for 'buf', got {}", assigns.len());
    }

    #[test]
    fn test_return_expression() {
        let tokens = tokenize("return optInTimePeriod - 1;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Return {
                value: Some(Expr::BinOp(left, BinOp::Sub, right)),
            } => {
                match left.as_ref() {
                    Expr::Var(s) => assert_eq!(s, "optInTimePeriod"),
                    other => panic!("Expected Var, got {other:?}"),
                }
                match right.as_ref() {
                    Expr::IntLiteral(1) => {}
                    other => panic!("Expected IntLiteral(1), got {other:?}"),
                }
            }
            other => panic!("Expected Return with BinOp, got {other:?}"),
        }
    }

    // ===== strip_local_macros: multi-line #define and #undef =====

    #[test]
    fn test_strip_local_macros_multiline_define() {
        // Lines 85-86, 93-94: multi-line #define with backslash continuations
        let input = "#define FOO(x) \\\n    (x + 1)\nint y;";
        let result = strip_local_macros(input);
        assert_eq!(result, "int y;");
    }

    #[test]
    fn test_strip_local_macros_undef() {
        let input = "#undef FOO\nint y;";
        let result = strip_local_macros(input);
        assert_eq!(result, "int y;");
    }

    #[test]
    fn test_strip_local_macros_undef_tab() {
        let input = "#undef\tFOO\nint y;";
        let result = strip_local_macros(input);
        assert_eq!(result, "int y;");
    }

    #[test]
    fn test_strip_local_macros_define_tab() {
        let input = "#define\tFOO 1\nint y;";
        let result = strip_local_macros(input);
        assert_eq!(result, "int y;");
    }

    #[test]
    fn test_strip_local_macros_multiline_continuation() {
        // A multi-line define where the continuation also ends with backslash
        let input = "#define FOO(x) \\\n    (x + 1) \\\n    + 2\nint z;";
        let result = strip_local_macros(input);
        assert_eq!(result, "int z;");
    }

    // ===== Tokenizer: special operators =====

    #[test]
    fn test_tokenize_left_shift() {
        // Line 277-278: << left shift operator
        let tokens = tokenize("x << 3");
        assert_eq!(
            tokens,
            vec![
                Token::Ident("x".to_string()),
                Token::Op("<<".to_string()),
                Token::IntNumber(3),
            ]
        );
    }

    #[test]
    fn test_tokenize_right_shift() {
        let tokens = tokenize("x >> 2");
        assert_eq!(
            tokens,
            vec![
                Token::Ident("x".to_string()),
                Token::Op(">>".to_string()),
                Token::IntNumber(2),
            ]
        );
    }

    #[test]
    fn test_tokenize_pipe() {
        // Lines 324-326: single | pipe token
        let tokens = tokenize("a | b");
        assert_eq!(
            tokens,
            vec![
                Token::Ident("a".to_string()),
                Token::Pipe,
                Token::Ident("b".to_string()),
            ]
        );
    }

    #[test]
    fn test_tokenize_dot() {
        // Lines 382-385: standalone . (dot) token
        let tokens = tokenize("arr.field");
        assert!(tokens.contains(&Token::Dot));
    }

    #[test]
    fn test_tokenize_preprocessor_skip() {
        // Lines 389-391, 393: # preprocessor directives get skipped
        let tokens = tokenize("#include <stdio.h>\nint x;");
        assert_eq!(
            tokens,
            vec![
                Token::Ident("int".to_string()),
                Token::Ident("x".to_string()),
                Token::Semicolon,
            ]
        );
    }

    #[test]
    fn test_tokenize_bang_equals() {
        let tokens = tokenize("x != y");
        assert!(tokens.contains(&Token::Op("!=".to_string())));
    }

    #[test]
    fn test_tokenize_percent() {
        let tokens = tokenize("x % 2");
        assert!(tokens.contains(&Token::Op("%".to_string())));
    }

    #[test]
    fn test_tokenize_float_with_f_suffix() {
        let tokens = tokenize("0.0f");
        assert_eq!(tokens, vec![Token::Number(0.0)]);
    }

    #[test]
    fn test_tokenize_leading_dot_float() {
        let tokens = tokenize(".5");
        assert_eq!(tokens, vec![Token::Number(0.5)]);
    }

    #[test]
    fn test_tokenize_ampersand() {
        let tokens = tokenize("&x");
        assert_eq!(
            tokens,
            vec![Token::Ampersand, Token::Ident("x".to_string())]
        );
    }

    #[test]
    fn test_tokenize_question_mark() {
        let tokens = tokenize("a ? b : c");
        assert!(tokens.contains(&Token::Question));
        assert!(tokens.contains(&Token::Colon));
    }

    // ===== Tokenizer panic =====

    #[test]
    #[should_panic(expected = "Unexpected character")]
    fn test_tokenize_unexpected_char() {
        // Line 396: unexpected character
        tokenize("int x = @;");
    }

    // ===== extract_func_params edge cases =====

    #[test]
    fn test_extract_func_params_no_lparen() {
        // Lines 413, 416: no LParen found returns empty
        let tokens = tokenize("int x;");
        let params = extract_func_params(&tokens, 0);
        assert!(params.is_empty());
    }

    #[test]
    fn test_extract_func_params_with_brackets() {
        // Lines 450: skip [] in param types like double buf[]
        let tokens = tokenize("(const double inReal[], int period)");
        let params = extract_func_params(&tokens, 0);
        assert_eq!(params.len(), 2);
        assert_eq!(params[0].0, "inReal");
        assert!(params[0].1.contains("[]"));
        assert_eq!(params[1].0, "period");
    }

    #[test]
    fn test_extract_func_params_with_star() {
        // Line 461: Star in parameter type
        let tokens = tokenize("(int *outBegIdx, double *outReal)");
        let params = extract_func_params(&tokens, 0);
        assert_eq!(params.len(), 2);
        assert_eq!(params[0].0, "outBegIdx");
        assert!(params[0].1.contains('*'));
        assert_eq!(params[1].0, "outReal");
    }

    // ===== Static TA_RetCode function detection =====

    #[test]
    fn test_static_ta_retcode_function() {
        // Lines 507-510: static TA_RetCode function detection
        let source = r"
static TA_RetCode helper_func(int startIdx, int endIdx, int *outBegIdx)
{
    *outBegIdx = startIdx;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        assert_eq!(parsed.functions.len(), 1);
        assert_eq!(parsed.functions[0].name, "helper_func");
    }

    // ===== Helper function parsing =====

    #[test]
    fn test_parse_helper_function_basic() {
        // Lines 558-631: extract_helper_functions
        let source = "double helper(double x, int y) { return x; }";
        let helpers = parse_helper_file_str(source);
        assert_eq!(helpers.len(), 1);
        assert_eq!(helpers[0].name, "helper");
        assert_eq!(helpers[0].return_type, VarType::Real);
        assert_eq!(helpers[0].params.len(), 2);
        assert_eq!(helpers[0].params[0].name, "x");
        assert_eq!(helpers[0].params[0].var_type, VarType::Real);
        assert_eq!(helpers[0].params[1].name, "y");
        assert_eq!(helpers[0].params[1].var_type, VarType::Integer);
    }

    #[test]
    fn test_parse_helper_function_int_return() {
        let source = "int helper(int a) { return a; }";
        let helpers = parse_helper_file_str(source);
        assert_eq!(helpers.len(), 1);
        assert_eq!(helpers[0].return_type, VarType::Integer);
    }

    #[test]
    fn test_parse_helper_function_no_brace() {
        // Lines 614-615: next must be { after params
        let source = "double helper(double x);";
        let helpers = parse_helper_file_str(source);
        assert!(helpers.is_empty());
    }

    #[test]
    fn test_parse_helper_function_not_a_function() {
        // Lines 576-577: next token not LParen
        let source = "double value = 1.0;";
        let helpers = parse_helper_file_str(source);
        assert!(helpers.is_empty());
    }

    #[test]
    fn test_parse_helper_function_bad_return_type() {
        // Lines 562-563: return type is not double or int
        let source = "void helper(int x) { return; }";
        let helpers = parse_helper_file_str(source);
        assert!(helpers.is_empty());
    }

    #[test]
    fn test_parse_helper_function_no_name() {
        // Lines 569-570: next token not identifier
        let source = "double (int x) { return x; }";
        let helpers = parse_helper_file_str(source);
        assert!(helpers.is_empty());
    }

    #[test]
    fn test_parse_helper_function_bad_param_type() {
        // Line 593: param type not double or int
        let source = "double helper(void x) { return x; }";
        let helpers = parse_helper_file_str(source);
        // Should parse as a helper with 0 params (breaks out of param loop)
        assert_eq!(helpers.len(), 0);
    }

    #[test]
    fn test_parse_helper_function_bad_param_name() {
        // Line 598: param name not identifier after type
        let source = "double helper(double 42) { return 0.0; }";
        let helpers = parse_helper_file_str(source);
        assert!(helpers.is_empty());
    }

    #[test]
    fn test_parse_helper_missing_matching_brace() {
        // Line 629: no matching brace
        let source = "double helper(double x) { return x;";
        let helpers = parse_helper_file_str(source);
        assert!(helpers.is_empty());
    }

    // ===== Break and Continue statements =====

    #[test]
    fn test_break_statement() {
        // Lines 776-778
        let tokens = tokenize("break;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        assert!(matches!(stmt, Statement::Break));
    }

    #[test]
    fn test_continue_statement() {
        // Lines 781-783
        let tokens = tokenize("continue;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        assert!(matches!(stmt, Statement::Continue));
    }

    // ===== Pre-increment/decrement as statement =====

    #[test]
    fn test_pre_increment_statement() {
        // Lines 798-812
        let tokens = tokenize("++x;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::Var(name),
                compound: true,
                value: Expr::BinOp(_, BinOp::Add, _),
            } => assert_eq!(name, "x"),
            other => panic!("Expected pre-increment Assign, got {other:?}"),
        }
    }

    #[test]
    fn test_pre_decrement_statement() {
        // Lines 816-830
        let tokens = tokenize("--x;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::Var(name),
                compound: true,
                value: Expr::BinOp(_, BinOp::Sub, _),
            } => assert_eq!(name, "x"),
            other => panic!("Expected pre-decrement Assign, got {other:?}"),
        }
    }

    // ===== Stray semicolons =====

    #[test]
    fn test_stray_semicolons() {
        // Lines 717-718: stray semicolons are skipped
        let source = r"
TA_RetCode test_func(int startIdx, int *outBegIdx)
{
    ;
    *outBegIdx = startIdx;
    ;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        // Should only have 2 statements: *outBegIdx = startIdx and return
        assert_eq!(body.len(), 2);
    }

    // ===== dedup_var_decls: non-VarDecl passthrough =====

    #[test]
    fn test_dedup_var_decls_non_vardecl_passthrough() {
        // Line 762: non-VarDecl statements pass through
        let source = r"
TA_RetCode test_func(int startIdx, int *outBegIdx)
{
    double x = 1.0;
    double x = 2.0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        // First double x is VarDecl, second is converted to Assign
        let var_count = body
            .iter()
            .filter(|s| matches!(s, Statement::VarDecl { name, .. } if name == "x"))
            .count();
        let assign_count = body
            .iter()
            .filter(|s| {
                matches!(s, Statement::Assign { target: Expr::Var(n), .. } if n == "x")
            })
            .count();
        assert_eq!(var_count, 1);
        assert_eq!(assign_count, 1);
    }

    // ===== Macro declarations =====

    #[test]
    fn test_enum_declaration_macro() {
        // Lines 884-907
        let tokens = tokenize("ENUM_DECLARATION(RetCode) retCode;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::VarDecl {
                var_type: VarType::RetCodeType,
                name,
                init: None,
            } => assert_eq!(name, "retCode"),
            other => panic!("Expected VarDecl with RetCodeType, got {other:?}"),
        }
    }

    #[test]
    fn test_enum_declaration_non_retcode() {
        // Line 899-901: non-RetCode enum type falls back to Integer
        let tokens = tokenize("ENUM_DECLARATION(MAType) maType;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::VarDecl {
                var_type: VarType::Integer,
                name,
                init: None,
            } => assert_eq!(name, "maType"),
            other => panic!("Expected VarDecl with Integer for non-RetCode, got {other:?}"),
        }
    }

    #[test]
    fn test_array_ref_macro() {
        // Lines 909-921
        let tokens = tokenize("ARRAY_REF(buf);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::VarDecl {
                var_type: VarType::Real,
                name,
                init: None,
            } => assert_eq!(name, "buf"),
            other => panic!("Expected VarDecl for ARRAY_REF, got {other:?}"),
        }
    }

    #[test]
    fn test_array_alloc_macro() {
        // Lines 923-945
        let tokens = tokenize("ARRAY_ALLOC(buf, 100);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, args)) => {
                assert_eq!(name, "ARRAY_ALLOC");
                assert!(args.is_empty());
            }
            other => panic!("Expected no-op Expr for ARRAY_ALLOC, got {other:?}"),
        }
    }

    #[test]
    fn test_array_free_macro() {
        let tokens = tokenize("ARRAY_FREE(buf);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, _)) => assert_eq!(name, "ARRAY_FREE"),
            other => panic!("Expected no-op for ARRAY_FREE, got {other:?}"),
        }
    }

    #[test]
    fn test_circbuf_macros() {
        use crate::ir::{CircBuf, CircBufLayout};

        // Plain scalar buffer (CCI shape): PROLOG / INIT / NEXT / DESTROY.
        let src = "CIRCBUF_PROLOG(circBuffer,double,30); \
                   CIRCBUF_INIT(circBuffer,double,optInTimePeriod); \
                   CIRCBUF_NEXT(circBuffer); \
                   CIRCBUF_DESTROY(circBuffer);";
        let mut parser = Parser::new(tokenize(src));
        let stmts = parser.parse_statements();
        match &stmts[0] {
            Statement::CircBuf(CircBuf::Prolog {
                id,
                layout,
                static_size,
            }) => {
                assert_eq!(id, "circBuffer");
                assert_eq!(*static_size, 30);
                assert!(matches!(layout, CircBufLayout::Plain(VarType::Real)));
            }
            other => panic!("expected Prolog, got {other:?}"),
        }
        assert!(
            matches!(&stmts[1], Statement::CircBuf(CircBuf::Init { id, .. }) if id == "circBuffer")
        );
        assert!(
            matches!(&stmts[2], Statement::CircBuf(CircBuf::Next { id }) if id == "circBuffer")
        );
        assert!(
            matches!(&stmts[3], Statement::CircBuf(CircBuf::Destroy { id, .. }) if id == "circBuffer")
        );

        // Class buffer (MFI shape): function-local typedef + PROLOG_CLASS + INIT_LOCAL_ONLY.
        let src2 = "typedef struct { double positive; double negative; } MoneyFlow; \
                    CIRCBUF_PROLOG_CLASS(mflow,MoneyFlow,50); \
                    CIRCBUF_INIT_LOCAL_ONLY(mflow,MoneyFlow);";
        let mut parser2 = Parser::new(tokenize(src2));
        let stmts2 = parser2.parse_statements();
        let prolog = stmts2
            .iter()
            .find_map(|s| match s {
                Statement::CircBuf(cb @ CircBuf::Prolog { .. }) => Some(cb),
                _ => None,
            })
            .expect("expected a Prolog");
        if let CircBuf::Prolog {
            layout: CircBufLayout::Class(fields),
            static_size,
            ..
        } = prolog
        {
            assert_eq!(*static_size, 50);
            assert_eq!(fields.len(), 2);
            assert_eq!(fields[0].0, "positive");
            assert_eq!(fields[1].0, "negative");
        } else {
            panic!("expected Class prolog, got {prolog:?}");
        }
        assert!(stmts2
            .iter()
            .any(|s| matches!(s, Statement::CircBuf(CircBuf::InitLocalOnly { .. }))));
    }

    #[test]
    fn test_constant_double_macro() {
        // Lines 947-961
        let tokens = tokenize("CONSTANT_DOUBLE(PI) = 3.14159;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::VarDecl {
                var_type: VarType::Real,
                name,
                init: Some(_),
            } => assert_eq!(name, "PI"),
            other => panic!("Expected VarDecl for CONSTANT_DOUBLE, got {other:?}"),
        }
    }

    #[test]
    fn test_constant_integer_macro() {
        // Lines 963-977
        let tokens = tokenize("CONSTANT_INTEGER(MAX_SIZE) = 100;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::VarDecl {
                var_type: VarType::Integer,
                name,
                init: Some(_),
            } => assert_eq!(name, "MAX_SIZE"),
            other => panic!("Expected VarDecl for CONSTANT_INTEGER, got {other:?}"),
        }
    }

    #[test]
    fn test_hilbert_variables_macro() {
        // Lines 990-1033
        let tokens = tokenize("HILBERT_VARIABLES(detrender);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Block { body } => {
                assert_eq!(body.len(), 6);
                // Check first and last names
                match &body[0] {
                    Statement::VarDecl { name, .. } => assert_eq!(name, "detrender_Odd0"),
                    other => panic!("Expected VarDecl, got {other:?}"),
                }
                match &body[5] {
                    Statement::VarDecl { name, .. } => assert_eq!(name, "detrender_Even2"),
                    other => panic!("Expected VarDecl, got {other:?}"),
                }
            }
            other => panic!("Expected Block for HILBERT_VARIABLES, got {other:?}"),
        }
    }

    #[test]
    fn test_init_hilbert_variables_macro() {
        // Lines 979-988
        let tokens = tokenize("INIT_HILBERT_VARIABLES(detrender);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, args)) => {
                assert_eq!(name, "INIT_HILBERT_VARIABLES");
                assert_eq!(args.len(), 1);
            }
            other => panic!("Expected FuncCall for INIT_HILBERT_VARIABLES, got {other:?}"),
        }
    }

    #[test]
    fn test_do_hilbert_odd_macro() {
        let tokens = tokenize("DO_HILBERT_ODD(a, b, c);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, args)) => {
                assert_eq!(name, "DO_HILBERT_ODD");
                assert_eq!(args.len(), 3);
            }
            other => panic!("Expected FuncCall for DO_HILBERT_ODD, got {other:?}"),
        }
    }

    #[test]
    fn test_do_hilbert_even_macro() {
        let tokens = tokenize("DO_HILBERT_EVEN(a, b, c);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, args)) => {
                assert_eq!(name, "DO_HILBERT_EVEN");
                assert_eq!(args.len(), 3);
            }
            other => panic!("Expected FuncCall for DO_HILBERT_EVEN, got {other:?}"),
        }
    }

    #[test]
    fn test_do_price_wma_macro() {
        let tokens = tokenize("DO_PRICE_WMA(a, b);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, args)) => {
                assert_eq!(name, "DO_PRICE_WMA");
                assert_eq!(args.len(), 2);
            }
            other => panic!("Expected FuncCall for DO_PRICE_WMA, got {other:?}"),
        }
    }

    // ===== Do-while loop =====

    #[test]
    fn test_do_while_loop() {
        // Lines 1239-1254
        let source = r"
TA_RetCode test_func(int startIdx, int *outBegIdx)
{
    int x;
    x = 0;
    do {
        x += 1;
    } while( x < 10 );
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        let do_while = body.iter().find(|s| matches!(s, Statement::DoWhile { .. }));
        assert!(do_while.is_some(), "Expected DoWhile statement");
        match do_while.unwrap() {
            Statement::DoWhile { body, .. } => assert_eq!(body.len(), 1),
            _ => unreachable!(),
        }
    }

    // ===== For loop: init with type declaration =====

    #[test]
    fn test_for_loop_with_type_decl_init() {
        // Lines 1340-1345: for init with type keyword
        let tokens = tokenize("for( int i = 0; i < 10; i++ ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { init, .. } => match *init {
                Statement::VarDecl {
                    var_type: VarType::Index,
                    name,
                    init: Some(_),
                } => assert_eq!(name, "i"),
                other => panic!("Expected VarDecl init, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== For loop: pre-decrement in update =====

    #[test]
    fn test_for_loop_pre_decrement_update() {
        // Lines 1370-1384: for update with --i
        let tokens = tokenize("for( i = 10; i > 0; --i ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { update, .. } => match *update {
                Statement::Assign {
                    compound: true,
                    value: Expr::BinOp(_, BinOp::Sub, _),
                    ..
                } => {}
                other => panic!("Expected pre-decrement update, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== For loop: pre-increment in update =====

    #[test]
    fn test_for_loop_pre_increment_update() {
        // Line 1357: for update with ++i
        let tokens = tokenize("for( i = 0; i < 10; ++i ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { update, .. } => match *update {
                Statement::Assign {
                    compound: true,
                    value: Expr::BinOp(_, BinOp::Add, _),
                    ..
                } => {}
                other => panic!("Expected pre-increment update, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== For loop: compound operator update =====

    #[test]
    fn test_for_loop_compound_op_update() {
        // Lines 1417-1426: for update with compound ops
        let tokens = tokenize("for( i = 0; i < 100; i += 5 ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { update, .. } => match *update {
                Statement::Assign {
                    compound: true,
                    value: Expr::BinOp(_, BinOp::Add, _),
                    ..
                } => {}
                other => panic!("Expected compound add update, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== For loop: simple assignment update =====

    #[test]
    fn test_for_loop_simple_assign_update() {
        // Lines 1430-1436: for update with simple assignment
        let tokens = tokenize("for( i = 0; i < 10; i = i + 1 ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { update, .. } => match *update {
                Statement::Assign {
                    compound: false,
                    target: Expr::Var(name),
                    ..
                } => assert_eq!(name, "i"),
                other => panic!("Expected simple assign update, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== For loop: post-decrement in update =====

    #[test]
    fn test_for_loop_post_decrement_update() {
        // Lines 1405-1416: i-- in for update
        let tokens = tokenize("for( i = 10; i > 0; i-- ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { update, .. } => match *update {
                Statement::Assign {
                    compound: true,
                    value: Expr::BinOp(_, BinOp::Sub, _),
                    ..
                } => {}
                other => panic!("Expected post-decrement update, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== For loop: braceless body =====

    #[test]
    fn test_for_loop_braceless_body() {
        // Lines 1323-1326: braceless for body
        let tokens = tokenize("for( i = 0; i < 10; i++ ) x = 1;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { body, .. } => assert_eq!(body.len(), 1),
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== For loop: multiple init and update =====

    #[test]
    fn test_for_loop_multi_init_and_update() {
        let tokens = tokenize("for( i = 0, j = 10; i < j; i++, j-- ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { init, update, .. } => {
                match *init {
                    Statement::Block { body } => assert_eq!(body.len(), 2),
                    other => panic!("Expected Block init, got {other:?}"),
                }
                match *update {
                    Statement::Block { body } => assert_eq!(body.len(), 2),
                    other => panic!("Expected Block update, got {other:?}"),
                }
            }
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== For loop: empty update =====

    #[test]
    fn test_for_loop_empty_update() {
        let tokens = tokenize("for( i = 0; i < 10; ) { i += 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { update, .. } => match *update {
                Statement::Block { body } => assert!(body.is_empty()),
                other => panic!("Expected empty Block update, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== If/else with braces =====

    #[test]
    fn test_if_else_with_braces() {
        // Lines 1468-1471: else { body }
        let tokens = tokenize("if( x > 0 ) { x = 1; } else { x = 2; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::If {
                then_body,
                else_body,
                ..
            } => {
                assert_eq!(then_body.len(), 1);
                assert_eq!(else_body.len(), 1);
            }
            other => panic!("Expected If, got {other:?}"),
        }
    }

    #[test]
    fn test_else_braceless_after_non_if_ident() {
        // Lines 1482: braceless else when peeked is not "if" and not LBrace
        // This tests the branch: else followed by non-if ident, non-brace token
        let tokens = tokenize("if( x > 0 ) { x = 1; } else x = 2;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::If {
                then_body,
                else_body,
                ..
            } => {
                assert_eq!(then_body.len(), 1);
                assert_eq!(else_body.len(), 1);
            }
            other => panic!("Expected If, got {other:?}"),
        }
    }

    // ===== Switch: ENUM_CASE macro =====

    #[test]
    fn test_switch_enum_case_macro() {
        // Lines 1518-1536: ENUM_CASE(Type, CName, PascalName)
        let tokens = tokenize(
            r"switch( maType ) {
                case ENUM_CASE(MAType, TA_MAType_SMA, Sma):
                    x = 1;
                    break;
                default:
                    x = 0;
                    break;
            }",
        );
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Switch { cases, .. } => {
                assert_eq!(cases.len(), 1);
                assert_eq!(cases[0].0, "MAType_SMA");
            }
            other => panic!("Expected Switch, got {other:?}"),
        }
    }

    #[test]
    fn test_switch_int_case_label() {
        // Line 1544-1545: case with IntNumber label
        let tokens = tokenize(
            r"switch( mode ) {
                case 0:
                    x = 1;
                    break;
                case 1:
                    x = 2;
                    break;
            }",
        );
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Switch { cases, .. } => {
                assert_eq!(cases.len(), 2);
                assert_eq!(cases[0].0, "0");
                assert_eq!(cases[1].0, "1");
            }
            other => panic!("Expected Switch, got {other:?}"),
        }
    }

    #[test]
    fn test_switch_ta_prefix_label() {
        // Lines 1537-1542: TA_ prefix stripping from case labels
        let tokens = tokenize(
            r"switch( maType ) {
                case TA_MAType_SMA:
                    x = 1;
                    break;
            }",
        );
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Switch { cases, .. } => {
                assert_eq!(cases[0].0, "MAType_SMA");
            }
            other => panic!("Expected Switch, got {other:?}"),
        }
    }

    #[test]
    fn test_switch_default_with_statements() {
        // Lines 1566-1580: default case with body before break
        let tokens = tokenize(
            r"switch( mode ) {
                default:
                    x = 0;
                    y = 1;
                    break;
            }",
        );
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Switch { default, .. } => {
                assert_eq!(default.len(), 2);
            }
            other => panic!("Expected Switch, got {other:?}"),
        }
    }

    #[test]
    fn test_switch_case_fallthrough_to_rbrace() {
        // Line 1552: case body ends at RBrace (no break)
        let tokens = tokenize(
            r"switch( mode ) {
                case MODE_A:
                    x = 1;
            }",
        );
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Switch { cases, .. } => {
                assert_eq!(cases.len(), 1);
                assert_eq!(cases[0].1.len(), 1);
            }
            other => panic!("Expected Switch, got {other:?}"),
        }
    }

    // ===== Bare return =====

    #[test]
    fn test_bare_return() {
        // Lines 1598-1599: return with no expression
        let tokens = tokenize("return;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Return { value: None } => {}
            other => panic!("Expected bare Return, got {other:?}"),
        }
    }

    // ===== Void cast =====

    #[test]
    fn test_void_cast_statement() {
        // Line 1648-1661
        let tokens = tokenize("(void)unused_var;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Block { body } => assert!(body.is_empty()),
            other => panic!("Expected empty Block for void cast, got {other:?}"),
        }
    }

    // ===== ALL_CAPS macro as standalone statement (no args) =====

    #[test]
    fn test_all_caps_macro_no_args() {
        // Lines 1672-1677: ALL_CAPS macro as standalone statement without parens
        let tokens = tokenize("CALCULATE_AD;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, args)) => {
                assert_eq!(name, "CALCULATE_AD");
                assert!(args.is_empty());
            }
            other => panic!("Expected macro FuncCall, got {other:?}"),
        }
    }

    // ===== ALL_CAPS macro with args =====

    #[test]
    fn test_all_caps_macro_with_args() {
        // Lines 1685-1700
        let tokens = tokenize("TRUE_RANGE(high, low, close, result);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, args)) => {
                assert_eq!(name, "TRUE_RANGE");
                assert_eq!(args.len(), 4);
            }
            other => panic!("Expected macro FuncCall with args, got {other:?}"),
        }
    }

    // ===== ALL_CAPS macro that backtracks =====

    #[test]
    fn test_all_caps_macro_backtrack() {
        // Line 1703: ALL_CAPS macro call followed by non-semicolon (e.g., an assignment op)
        // triggers backtrack to save_pos. After backtrack, it then falls through to the
        // function call path (1737) which also backtracks, and then to array access.
        // Use a pattern like FOO(x) that backtracks from ALL_CAPS macro but resolves as
        // a function call via the regular function call path (line 1737).
        // FOO[0] = 1; - FOO is ALL_CAPS, next is [, not ( so it bypasses the macro path.
        // We need ALL_CAPS followed by ( args ) then non-semicolon.
        // Actually, the scenario is: ALL_CAPS(args) followed by an operator like =
        // This backtracks from macro path (line 1703), then tries function call path
        // (line 1737), which also backtracks (line 1798), then tries array access (1802)
        // which doesn't match, then reaches assignment (1878).
        // Let's just test the simpler path: a function call that ends the token stream
        let tokens = tokenize("SOME_MACRO(x, y)");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, args)) => {
                assert_eq!(name, "SOME_MACRO");
                assert_eq!(args.len(), 2);
            }
            other => panic!("Expected FuncCall statement, got {other:?}"),
        }
    }

    // ===== Function call as statement =====

    #[test]
    fn test_function_call_as_statement() {
        // Lines 1737-1752
        let tokens = tokenize("some_func(x, y);");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(name, args)) => {
                assert_eq!(name, "some_func");
                assert_eq!(args.len(), 2);
            }
            other => panic!("Expected FuncCall statement, got {other:?}"),
        }
    }

    // ===== CIRCBUF_REF in statement context =====

    #[test]
    fn test_circbuf_ref_assign_statement() {
        // Lines 1756-1776: CIRCBUF_REF(arr[idx])field = expr;
        let tokens = tokenize("CIRCBUF_REF(buf[i])value = 3.14;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::ArrayAccess(name, _),
                compound: false,
                ..
            } => assert_eq!(name, "buf_value"),
            other => panic!("Expected CIRCBUF_REF assign, got {other:?}"),
        }
    }

    #[test]
    fn test_circbuf_ref_compound_assign_statement() {
        // Lines 1778-1790: CIRCBUF_REF(arr[idx])field += expr;
        let tokens = tokenize("CIRCBUF_REF(buf[i])total += 1.0;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::ArrayAccess(name, _),
                compound: true,
                ..
            } => assert_eq!(name, "buf_total"),
            other => panic!("Expected CIRCBUF_REF compound assign, got {other:?}"),
        }
    }

    #[test]
    fn test_circbuf_ref_fallback_no_array_arg() {
        // Lines 1764: fallback when CIRCBUF_REF arg isn't ArrayAccess
        let tokens = tokenize("CIRCBUF_REF(x)field = 1.0;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::Var(name),
                compound: false,
                ..
            } => assert_eq!(name, "circbuf_field"),
            other => panic!("Expected fallback CIRCBUF_REF assign, got {other:?}"),
        }
    }

    // ===== Array access with struct member =====

    #[test]
    fn test_array_struct_member_assign() {
        // Lines 1810-1840: arr[idx].field = expr;
        let tokens = tokenize("data[i].value = 1.0;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::ArrayAccess(name, _),
                compound: false,
                ..
            } => assert_eq!(name, "data_value"),
            other => panic!("Expected flattened struct member assign, got {other:?}"),
        }
    }

    #[test]
    fn test_array_struct_member_compound_assign() {
        // Lines 1817-1830: arr[idx].field += expr;
        let tokens = tokenize("data[i].total += 5.0;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::ArrayAccess(name, _),
                compound: true,
                ..
            } => assert_eq!(name, "data_total"),
            other => panic!("Expected flattened struct compound assign, got {other:?}"),
        }
    }

    // ===== Custom type variable declaration =====

    #[test]
    fn test_custom_type_var_decl() {
        // Lines 1914-1919: custom type like `MoneyFlow mflow[50];`
        let source = r"
TA_RetCode test_func(int startIdx, int *outBegIdx)
{
    MoneyFlow mflow;
    *outBegIdx = startIdx;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        // The MoneyFlow decl becomes an empty Block (no-op)
        let has_block = body.iter().any(|s| matches!(s, Statement::Block { body } if body.is_empty()));
        assert!(has_block, "Expected empty Block for custom type decl");
    }

    // ===== Chained assignment =====

    #[test]
    fn test_chained_assignment() {
        let source = r"
TA_RetCode test_func(int startIdx, int *outBegIdx)
{
    double a;
    double b;
    double c;
    a = b = c = 0.0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        // a = b = c = 0.0 should produce a Block with multiple assignments
        let chain = body.iter().find(|s| matches!(s, Statement::Block { .. }));
        assert!(chain.is_some(), "Expected chained assignment Block");
    }

    // ===== Bitwise OR =====

    #[test]
    fn test_bitwise_or_expr() {
        // Lines 1996-1998
        let tokens = tokenize("a | b | c");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::BinOp(_, BinOp::BitwiseOr, _) => {}
            other => panic!("Expected BitwiseOr, got {other:?}"),
        }
    }

    // ===== Shift operators in expressions =====

    #[test]
    fn test_shift_operators() {
        let tokens = tokenize("x << 2");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::BinOp(_, BinOp::Shl, _) => {}
            other => panic!("Expected Shl, got {other:?}"),
        }

        let tokens2 = tokenize("x >> 1");
        let mut parser2 = Parser::new(tokens2);
        let expr2 = parser2.parse_expr();
        match expr2 {
            Expr::BinOp(_, BinOp::Shr, _) => {}
            other => panic!("Expected Shr, got {other:?}"),
        }
    }

    // ===== Ternary expression =====

    #[test]
    fn test_ternary_expr() {
        let tokens = tokenize("x > 0 ? 1 : 0");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::Ternary(_, _, _) => {}
            other => panic!("Expected Ternary, got {other:?}"),
        }
    }

    // ===== Unary minus and plus =====

    #[test]
    fn test_unary_minus() {
        let tokens = tokenize("-x");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::BinOp(left, BinOp::Sub, _) => match *left {
                Expr::IntLiteral(0) => {}
                other => panic!("Expected IntLiteral(0) for unary minus, got {other:?}"),
            },
            other => panic!("Expected Sub from 0 for unary minus, got {other:?}"),
        }
    }

    #[test]
    fn test_unary_plus() {
        let tokens = tokenize("+x");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::Var(name) => assert_eq!(name, "x"),
            other => panic!("Expected Var for unary plus, got {other:?}"),
        }
    }

    // ===== Pre-increment/decrement in expression =====

    #[test]
    fn test_pre_increment_expr() {
        let tokens = tokenize("++x");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::PreIncrement(_) => {}
            other => panic!("Expected PreIncrement, got {other:?}"),
        }
    }

    #[test]
    fn test_pre_decrement_expr() {
        let tokens = tokenize("--x");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::PreDecrement(_) => {}
            other => panic!("Expected PreDecrement, got {other:?}"),
        }
    }

    // ===== Dereference of parenthesized expression =====

    #[test]
    fn test_deref_parenthesized() {
        // Lines 2098-2100: *(ptr)
        let tokens = tokenize("*(ptr)");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::PointerDeref(name) => assert_eq!(name, "ptr"),
            other => panic!("Expected PointerDeref, got {other:?}"),
        }
    }

    // ===== CIRCBUF_REF in expression context =====

    #[test]
    fn test_circbuf_ref_expr() {
        // Lines 2164-2170: CIRCBUF_REF(arr[idx])field in expression
        let tokens = tokenize("CIRCBUF_REF(buf[i])value");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::ArrayAccess(name, _) => assert_eq!(name, "buf_value"),
            other => panic!("Expected ArrayAccess for CIRCBUF_REF expr, got {other:?}"),
        }
    }

    #[test]
    fn test_circbuf_ref_expr_fallback() {
        // Line 2174: CIRCBUF_REF fallback when no field follows
        let tokens = tokenize("CIRCBUF_REF(x)");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::FuncCall(name, _) => assert_eq!(name, "CIRCBUF_REF"),
            other => panic!("Expected FuncCall fallback for CIRCBUF_REF, got {other:?}"),
        }
    }

    // ===== Array access with struct member in expression =====

    #[test]
    fn test_array_struct_member_expr() {
        // Lines 2187-2192: arr[idx].field in expression
        let tokens = tokenize("data[i].value");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::ArrayAccess(name, _) => assert_eq!(name, "data_value"),
            other => panic!("Expected flattened struct ArrayAccess, got {other:?}"),
        }
    }

    // ===== Post-increment/decrement on array access =====

    #[test]
    fn test_array_post_increment_expr() {
        // Lines 2199-2200
        let tokens = tokenize("arr[i]++");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::PostIncrement(inner) => match *inner {
                Expr::ArrayAccess(name, _) => assert_eq!(name, "arr"),
                other => panic!("Expected ArrayAccess, got {other:?}"),
            },
            other => panic!("Expected PostIncrement, got {other:?}"),
        }
    }

    #[test]
    fn test_array_post_decrement_expr() {
        // Lines 2203-2204
        let tokens = tokenize("arr[i]--");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::PostDecrement(inner) => match *inner {
                Expr::ArrayAccess(name, _) => assert_eq!(name, "arr"),
                other => panic!("Expected ArrayAccess, got {other:?}"),
            },
            other => panic!("Expected PostDecrement, got {other:?}"),
        }
    }

    // ===== Post-increment/decrement on plain identifier in expression =====

    #[test]
    fn test_post_decrement_expr() {
        let tokens = tokenize("x--");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::PostDecrement(inner) => match *inner {
                Expr::Var(name) => assert_eq!(name, "x"),
                other => panic!("Expected Var, got {other:?}"),
            },
            other => panic!("Expected PostDecrement, got {other:?}"),
        }
    }

    // ===== Address-of expression =====

    #[test]
    fn test_address_of_expr() {
        let tokens = tokenize("&x");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::AddressOf(inner) => match *inner {
                Expr::Var(name) => assert_eq!(name, "x"),
                other => panic!("Expected Var in AddressOf, got {other:?}"),
            },
            other => panic!("Expected AddressOf, got {other:?}"),
        }
    }

    // ===== Modulo operator =====

    #[test]
    fn test_modulo_expr() {
        let tokens = tokenize("x % 3");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::BinOp(_, BinOp::Mod, _) => {}
            other => panic!("Expected Mod, got {other:?}"),
        }
    }

    // ===== Logical OR =====

    #[test]
    fn test_logical_or() {
        // Line 1971
        let tokens = tokenize("a || b");
        let mut parser = Parser::new(tokens);
        let expr = parser.parse_expr();
        match expr {
            Expr::BinOp(_, BinOp::Or, _) => {}
            other => panic!("Expected Or, got {other:?}"),
        }
    }

    // ===== Var decl: pointer to index type =====

    #[test]
    fn test_var_decl_pointer_to_index() {
        // Line 1084: pointer to non-real/non-int stays as-is
        let tokens = tokenize("size_t *ptr;");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl { var_type, name, .. } => {
                // size_t * should keep as Index (the "other" branch)
                assert_eq!(*var_type, VarType::Index);
                assert_eq!(name, "ptr");
            }
            other => panic!("Expected VarDecl, got {other:?}"),
        }
    }

    // ===== Var decl: int pointer =====

    #[test]
    fn test_var_decl_int_pointer() {
        let tokens = tokenize("int *buf;");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl { var_type, .. } => {
                assert_eq!(*var_type, VarType::IntPointer);
            }
            other => panic!("Expected VarDecl with IntPointer, got {other:?}"),
        }
    }

    // ===== Var decl: double pointer =====

    #[test]
    fn test_var_decl_double_pointer() {
        let tokens = tokenize("double *buf;");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl { var_type, .. } => {
                assert_eq!(*var_type, VarType::RealPointer);
            }
            other => panic!("Expected VarDecl with RealPointer, got {other:?}"),
        }
    }

    // ===== Var decl: fixed-size array =====

    #[test]
    fn test_var_decl_fixed_size_real_array() {
        // Lines 1121-1155: array declarations
        let tokens = tokenize("double buf[30];");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl {
                var_type: VarType::RealArray(size),
                ..
            } => assert_eq!(size, "30"),
            other => panic!("Expected RealArray, got {other:?}"),
        }
    }

    #[test]
    fn test_var_decl_fixed_size_int_array() {
        let tokens = tokenize("int buf[3];");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl {
                var_type: VarType::IntArray(size),
                ..
            } => assert_eq!(size, "3"),
            other => panic!("Expected IntArray, got {other:?}"),
        }
    }

    #[test]
    fn test_var_decl_array_with_expr_size() {
        // Lines 1140-1149: array size with identifier expression
        let tokens = tokenize("double buf[MAX_SIZE];");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl {
                var_type: VarType::RealArray(size),
                ..
            } => assert_eq!(size, "MAX_SIZE"),
            other => panic!("Expected RealArray with ident size, got {other:?}"),
        }
    }

    #[test]
    fn test_var_decl_index_array_stays_index() {
        // Line 1155: array of size_t stays Index (the "other" branch)
        let tokens = tokenize("size_t arr[5];");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl { var_type, .. } => {
                assert_eq!(*var_type, VarType::Index);
            }
            other => panic!("Expected VarDecl with Index type, got {other:?}"),
        }
    }

    // ===== Array compound assignment =====

    #[test]
    fn test_array_compound_assign() {
        // Lines 1847-1862: arr[i] += expr
        let tokens = tokenize("data[i] += 1.0;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::ArrayAccess(name, _),
                compound: true,
                ..
            } => assert_eq!(name, "data"),
            other => panic!("Expected array compound assign, got {other:?}"),
        }
    }

    // ===== Compound assignment with chained rhs =====

    #[test]
    fn test_compound_assign_with_chain() {
        // Lines 1894-1910: SumY += tempValue1 = inReal[i]
        let source = r"
TA_RetCode test_func(int startIdx, int *outBegIdx)
{
    double sumY;
    double tempValue1;
    sumY += tempValue1 = 1.0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        // sumY += tempValue1 = 1.0 => Block with [tempValue1 = 1.0, sumY += tempValue1]
        let block = body.iter().find(|s| matches!(s, Statement::Block { body } if body.len() == 2));
        assert!(block.is_some(), "Expected Block from chained compound assign");
    }

    // ===== Pointer deref with chained assignment =====

    #[test]
    fn test_pointer_deref_chained_assign() {
        let source = r"
TA_RetCode test_func(int *outBegIdx)
{
    double x;
    *outBegIdx = x = 0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        let block = body.iter().find(|s| matches!(s, Statement::Block { body } if body.len() == 2));
        assert!(
            block.is_some(),
            "Expected Block from chained pointer deref assign"
        );
    }

    // ===== Anonymous block =====

    #[test]
    fn test_anonymous_block() {
        let source = r"
TA_RetCode test_func(int startIdx, int *outBegIdx)
{
    { double x; x = 1.0; }
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        let has_block = body.iter().any(|s| matches!(s, Statement::Block { body } if !body.is_empty()));
        assert!(has_block, "Expected anonymous block in body");
    }

    // ===== Const variable declaration =====

    #[test]
    fn test_const_var_decl() {
        let source = r"
TA_RetCode test_func(int startIdx, int *outBegIdx)
{
    const double x = 1.0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        match &body[0] {
            Statement::VarDecl {
                var_type: VarType::Real,
                name,
                init: Some(_),
            } => assert_eq!(name, "x"),
            other => panic!("Expected const VarDecl, got {other:?}"),
        }
    }

    // ===== is_all_caps_macro =====

    #[test]
    fn test_is_all_caps_macro() {
        assert!(Parser::is_all_caps_macro("CALCULATE_AD"));
        assert!(Parser::is_all_caps_macro("TRUE_RANGE"));
        assert!(Parser::is_all_caps_macro("MAX123"));
        assert!(!Parser::is_all_caps_macro("x")); // single char
        assert!(!Parser::is_all_caps_macro("camelCase"));
        assert!(!Parser::is_all_caps_macro("a")); // too short
    }

    // ===== strip_ta_prefix with TA_COMPATIBILITY_ =====

    #[test]
    fn test_strip_ta_compatibility_prefix() {
        assert_eq!(strip_ta_prefix("TA_COMPATIBILITY_DEFAULT"), "DEFAULT");
        assert_eq!(strip_ta_prefix("TA_COMPATIBILITY_METASTOCK"), "METASTOCK");
    }

    // ===== transform_func_name =====

    #[test]
    fn test_transform_func_name() {
        assert_eq!(transform_func_name("TA_GetUnstablePeriod"), "UNSTABLE_PERIOD");
        assert_eq!(transform_func_name("TA_GetCompatibility"), "COMPATIBILITY");
        assert_eq!(transform_func_name("TA_SMA"), "SMA");
        assert_eq!(transform_func_name("plain_func"), "plain_func");
    }

    // ===== Unguarded function detection =====

    #[test]
    fn test_private_function_detection() {
        let source = r"
TA_RetCode sma_private(int startIdx, int endIdx, int *outBegIdx)
{
    *outBegIdx = startIdx;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        assert_eq!(parsed.functions.len(), 1);
        assert_eq!(parsed.functions[0].name, "sma_private");
    }

    // ===== Braceless while =====

    #[test]
    fn test_braceless_while() {
        let tokens = tokenize("while( x < 10 ) x += 1;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::While { body, .. } => assert_eq!(body.len(), 1),
            other => panic!("Expected While, got {other:?}"),
        }
    }

    // ===== Post-decrement as statement =====

    #[test]
    fn test_post_decrement_statement() {
        let tokens = tokenize("x--;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::Var(name),
                compound: true,
                value: Expr::BinOp(_, BinOp::Sub, _),
            } => assert_eq!(name, "x"),
            other => panic!("Expected post-decrement Assign, got {other:?}"),
        }
    }

    // ===== Function call at end of tokens =====

    #[test]
    fn test_function_call_at_end_of_tokens() {
        // Line 1744: self.pos >= self.tokens.len()
        let tokens = tokenize("func(x)");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Expr(Expr::FuncCall(_, _)) => {}
            other => panic!("Expected FuncCall at end of tokens, got {other:?}"),
        }
    }

    // ===== Var decl: array size tokens with operators =====

    #[test]
    fn test_var_decl_array_complex_size() {
        // Lines 1143-1148: various token types in array size
        let tokens = tokenize("double buf[N * 2 + 1];");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl {
                var_type: VarType::RealArray(size),
                ..
            } => {
                assert!(size.contains('N'));
                assert!(size.contains('*'));
                assert!(size.contains('+'));
            }
            other => panic!("Expected RealArray with complex size, got {other:?}"),
        }
    }

    // ===== find_open_brace and find_matching_brace with no brace found =====

    #[test]
    fn test_find_open_brace_none() {
        // Line 645: no brace found returns None
        let tokens = tokenize("int x;");
        assert_eq!(find_open_brace(&tokens, 0), None);
    }

    #[test]
    fn test_find_matching_brace_none() {
        // Line 665: unmatched brace returns None
        let tokens = tokenize("{ x = 1;");
        // Position 0 is LBrace, but there's no matching RBrace
        assert_eq!(find_matching_brace(&tokens, 0), None);
    }

    // ===== For update compound operators: -=, *=, /= =====

    #[test]
    fn test_for_update_subtract_compound() {
        let tokens = tokenize("for( i = 10; i > 0; i -= 2 ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { update, .. } => match *update {
                Statement::Assign {
                    compound: true,
                    value: Expr::BinOp(_, BinOp::Sub, _),
                    ..
                } => {}
                other => panic!("Expected -= update, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    #[test]
    fn test_for_update_multiply_compound() {
        let tokens = tokenize("for( i = 1; i < 1000; i *= 2 ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { update, .. } => match *update {
                Statement::Assign {
                    compound: true,
                    value: Expr::BinOp(_, BinOp::Mul, _),
                    ..
                } => {}
                other => panic!("Expected *= update, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    #[test]
    fn test_for_update_divide_compound() {
        let tokens = tokenize("for( i = 100; i > 0; i /= 2 ) { x = 1; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::ForC { update, .. } => match *update {
                Statement::Assign {
                    compound: true,
                    value: Expr::BinOp(_, BinOp::Div, _),
                    ..
                } => {}
                other => panic!("Expected /= update, got {other:?}"),
            },
            other => panic!("Expected ForC, got {other:?}"),
        }
    }

    // ===== Else-if chain =====

    #[test]
    fn test_else_if_chain() {
        let tokens = tokenize("if( x > 0 ) { x = 1; } else if( x < 0 ) { x = 2; } else { x = 0; }");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::If {
                then_body,
                else_body,
                ..
            } => {
                assert_eq!(then_body.len(), 1);
                // else_body should contain a single If statement
                assert_eq!(else_body.len(), 1);
                match &else_body[0] {
                    Statement::If { else_body, .. } => {
                        assert_eq!(else_body.len(), 1);
                    }
                    other => panic!("Expected nested If, got {other:?}"),
                }
            }
            other => panic!("Expected If, got {other:?}"),
        }
    }

    // ===== Switch default that ends at RBrace =====

    #[test]
    fn test_switch_default_ends_at_rbrace() {
        // Line 1571: default body ends at RBrace without explicit break
        let tokens = tokenize(
            r"switch( mode ) {
                default:
                    x = 0;
            }",
        );
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Switch { default, .. } => {
                assert_eq!(default.len(), 1);
            }
            other => panic!("Expected Switch, got {other:?}"),
        }
    }

    // ===== Array access with -= compound in statement =====

    #[test]
    fn test_array_subtract_compound_assign() {
        let tokens = tokenize("data[i] -= 2.0;");
        let mut parser = Parser::new(tokens);
        let stmt = parser.parse_statement();
        match stmt {
            Statement::Assign {
                target: Expr::ArrayAccess(name, _),
                compound: true,
                value: Expr::BinOp(_, BinOp::Sub, _),
            } => assert_eq!(name, "data"),
            other => panic!("Expected array subtract compound, got {other:?}"),
        }
    }

    // ===== Helper function: extract_func_params with empty params =====

    #[test]
    fn test_extract_func_params_void() {
        let tokens = tokenize("(void)");
        let params = extract_func_params(&tokens, 0);
        // void is treated as a type token but no name follows, so empty params
        assert!(params.is_empty());
    }

    // ===== Index var name detection =====

    #[test]
    fn test_is_index_var_name() {
        assert!(Parser::is_index_var_name("i"));
        assert!(Parser::is_index_var_name("j"));
        assert!(Parser::is_index_var_name("k"));
        assert!(Parser::is_index_var_name("outIdx"));
        assert!(Parser::is_index_var_name("today"));
        assert!(Parser::is_index_var_name("todayIdx"));
        assert!(Parser::is_index_var_name("trailingIdx"));
        assert!(Parser::is_index_var_name("inIdx"));
        assert!(Parser::is_index_var_name("totIdx"));
        assert!(Parser::is_index_var_name("idx"));
        assert!(Parser::is_index_var_name("BodyLongTrailingIdx"));
        assert!(!Parser::is_index_var_name("count"));
        assert!(!Parser::is_index_var_name("period"));
    }

    // ===== TA_RetCode var type =====

    #[test]
    fn test_ta_retcode_var_type() {
        let tokens = tokenize("TA_RetCode rc;");
        let mut parser = Parser::new(tokens);
        let stmts = parser.parse_var_decl();
        assert_eq!(stmts.len(), 1);
        match &stmts[0] {
            Statement::VarDecl { var_type, .. } => assert_eq!(*var_type, VarType::RetCodeType),
            other => panic!("Expected VarDecl, got {other:?}"),
        }
    }

    // ===== Complex chained assignment: simple = ... =====

    #[test]
    fn test_simple_assignment_plain_rhs() {
        let source = r"
TA_RetCode test_func(int startIdx, int *outBegIdx)
{
    double x;
    x = 5.0;
    return TA_SUCCESS;
}
";
        let parsed = parse_c_source_str(source);
        let body = &parsed.functions[0].body;
        match &body[1] {
            Statement::Assign {
                target: Expr::Var(name),
                value: Expr::Literal(n),
                compound: false,
            } => {
                assert_eq!(name, "x");
                assert!((n - 5.0).abs() < f64::EPSILON);
            }
            other => panic!("Expected simple Assign, got {other:?}"),
        }
    }
}
