// rasm-core/src/parser/ast.rs
//
//! # AST (Abstract Syntax Tree)
//!
//! Программа на ассемблере — это плоский список утверждений (Statement).
//! Никаких вложенных блоков, никаких функций с телами — только строка за строкой.
use crate::{
    isa::{Instruction, directive::Directive},
    lexer::token::Span,
    str_pool::StrId,
};

/// Корень AST. Содержит все Statement-ы программы в том порядке, в котором
/// они встретились в исходном коде.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AST {
    pub program: Vec<Statement>,
}

impl AST {
    pub fn new() -> Self {
        Self {
            program: Vec::new(),
        }
    }
}

impl Default for AST {
    fn default() -> Self {
        Self::new()
    }
}

/// Одна логическая единица программы (обычно — одна строка).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Statement {
    /// Определение метки: `main:` или `.loop_start:`
    Label { span: Span, name: StrId },

    /// Процессорная инструкция: `mov rax, rbx`, `syscall`, `ret`
    Instruction {
        span: Span,
        instruction: Instruction,
    },

    /// Директива компилятора: `.text`, `.global _start`, `.quad 42`
    Directive { span: Span, directive: Directive },
}

/// Константное выражение, вычисляемое во время сборки.
/// Используется, например, для: `BASE + 0x10` или `1 << 4`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstExpr {
    Immediate(i64),
    Symbol(StrId),
    BinaryOp {
        op: BinOp,
        left: Box<Self>,
        right: Box<Self>,
    },
}

/// Арифметические операции, поддерживаемые в константных выражениях.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinOp {
    Add,
    Sub,
    Mul,
    Div,
}

/// Ошибка вычисления константного выражения.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstExprError {
    DivisionByZero,
    SymbolNotResolved(StrId),
}

impl ConstExpr {
    /// Вычисляет значение константного выражения.
    /// Пока не поддерживает ссылки на метки (Symbol),
    /// но закладываем интерфейс на будущее.
    pub fn evaluate(&self) -> Result<i64, ConstExprError> {
        match self {
            ConstExpr::Immediate(val) => Ok(*val),
            ConstExpr::Symbol(id) => Err(ConstExprError::SymbolNotResolved(*id)),
            ConstExpr::BinaryOp { op, left, right } => {
                let l = left.evaluate()?;
                let r = right.evaluate()?;
                match op {
                    BinOp::Add => Ok(l.wrapping_add(r)),
                    BinOp::Sub => Ok(l.wrapping_sub(r)),
                    BinOp::Mul => Ok(l.wrapping_mul(r)),
                    BinOp::Div => {
                        if r == 0 {
                            Err(ConstExprError::DivisionByZero)
                        } else {
                            Ok(l.wrapping_div(r))
                        }
                    }
                }
            }
        }
    }
}
