librasm 0.0.0

Library for using RASM.
//! Библиотека для удобного использования `RASM`.
use std::fmt;

use rasm_core::{
    codegen::{CodeGen, CodegenOutput},
    error::{Error, ErrorKind},
    lexer::{Lexer, token::Token},
    parser::{Parser, ast::AST},
    src::SourceCode,
    str_pool::StrPool,
};

pub enum SourceASM<'a> {
    String(String),
    Slice(&'a str),
    StaticSlice(&'static str),
    SourceCode(SourceCode),
}

impl<'a> fmt::Display for SourceASM<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            // В обоих случаях мы просто передаем строку во форматировщик
            Self::String(s) => write!(f, "{s}"),
            Self::Slice(s) => write!(f, "{s}"),
            Self::StaticSlice(s) => write!(f, "{s}"),
            Self::SourceCode(s) => write!(f, "{s}"),
        }
    }
}

pub enum RASMDebugStagies {
    Tokens,
    AST,
    Bytes,
}

pub struct RASMDebug {
    pub stage: RASMDebugStagies,
    pub until_stage: bool,
}

pub struct RASM {
    /// # Пул строк
    ///
    /// Все строки хранятся в `str_pool`, и обращаются все тоже к нему.
    pub str_pool: StrPool,

    /// Исходный код.
    pub src: SourceCode,

    tokens: Vec<Token>,
    ast: AST,
}

impl RASM {
    pub fn new(src: SourceASM) -> Self {
        let src = SourceCode::new(src.to_string());
        Self {
            str_pool: StrPool::from_source(&src),
            src,
            tokens: Vec::new(),
            ast: AST::new(),
        }
    }

    /// # Токенизация кода
    ///
    /// Вызывает [`Lexer::tokenize()`].
    ///
    /// ## Что возвращает
    /// - [`Vec<Error>`] — если вектор ошибок в `Lexer.errors` имеет хотя бы одну ошибку.
    /// - [`Vec<Token>`] — если ошибок нету.
    ///
    /// ## Пример использования
    /// ```
    /// use librasm::{RASM, SourceASM};
    ///
    /// fn main() {
    ///     let code = SourceASM::Slice("_start:
    ///     mov rax, 0 ; В Windows x64 ABI регистр RAX является основным регистром для возврата скалярных значений (целых чисел, указателей) размером до 64 бит из любой функции.
    ///     ret");
    ///     
    ///     let mut rasm = RASM::new(code);
    ///
    ///     // Токенизируем код
    ///     let tokenize_result = rasm.tokenize();
    ///
    ///     assert!(tokenize_result.is_ok());
    /// }
    /// ```
    pub fn tokenize(&mut self) -> Result<Vec<Token>, Vec<Error<'_>>> {
        let mut lexer = Lexer::new(&mut self.str_pool, self.src.clone());
        lexer.tokenize();

        if lexer.errors.is_empty() {
            self.tokens = lexer.tokens.clone();
            Ok(lexer.tokens)
        } else {
            let errors: Vec<Error> = lexer
                .errors
                .into_iter()
                .map(|lexer_err| {
                    let position = lexer_err.pos;
                    Error::new(ErrorKind::Lexer(lexer_err), position, &self.src)
                })
                .collect();

            Err(errors)
        }
    }

    /// # Построение AST
    ///
    /// Вызывает [`Parser::parse()`].
    ///
    /// ## Что возвращает
    /// - [`AST`] — если парсинг прошёл успешно.
    /// - [`Vec<Error>`] — если есть ошибки.
    pub fn parse(&mut self) -> Result<AST, Vec<Error<'_>>> {
        let mut parser = Parser::new(&self.tokens, &mut self.str_pool);
        let ast = parser.parse();

        if parser.errors.is_empty() {
            self.ast = ast.clone();
            Ok(ast)
        } else {
            let errors: Vec<Error> = parser
                .errors
                .into_iter()
                .map(|parser_err| {
                    let position = parser_err.pos;
                    Error::new(ErrorKind::Parser(parser_err), position, &self.src)
                })
                .collect();

            Err(errors)
        }
    }

    /// # Кодогенерация
    ///
    /// Принимает готовый [`AST`] (из [`Self::parse()`]) и запускает [`CodeGen::compile()`].
    ///
    /// ## Что возвращает
    /// - [`CodegenOutput`] — байтовый буфер с машинным кодом и таблица меток, если ошибок нет.
    /// - [`Vec<Error>`] — если кодогенератор нашёл ошибки.
    ///
    /// ## Пример полного пайплайна
    ///
    /// ```
    /// use librasm::{RASM, SourceASM};
    ///
    /// let src = SourceASM::Slice("mov rax, 1\nret\n");
    /// let mut rasm = RASM::new(src);
    ///
    /// let _tokens = rasm.tokenize().unwrap(); // Токенизируем, чтобы токены появились в структуре
    /// let ast = rasm.parse().unwrap();
    /// let output = rasm.compile().unwrap();
    ///
    /// println!("code bytes: {:?}", output.code);
    /// ```
    pub fn compile(&self) -> Result<CodegenOutput, Vec<Error<'_>>> {
        let mut codegen = CodeGen::new(&self.str_pool);
        let output = codegen.compile(&self.ast);

        if codegen.errors.is_empty() {
            Ok(output)
        } else {
            let errors: Vec<Error> = codegen
                .errors
                .into_iter()
                .map(|cg_err| {
                    // У CodegenError нет Span — ошибка относится к инструкции в целом,
                    // а не к конкретному байту. Используем нулевой Span как маркер.
                    Error::new(ErrorKind::Codegen(cg_err), Default::default(), &self.src)
                })
                .collect();

            Err(errors)
        }
    }
    /// # Отладочный вывод
    ///
    /// Прогоняет пайплайн до указанной стадии и печатает результат в stderr.
    ///
    /// ## Параметр `until_stage`
    /// - `true`  — печатает **все** стадии до указанной включительно.
    /// - `false` — печатает **только** указанную стадию.
    ///
    /// ## Пример
    /// ```ignore
    /// use librasm::{SourceASM, RASM};
    ///
    /// let src = SourceASM::Slice("mov rax, 1\nret\n");
    /// let mut rasm = RASM::new(src);
    ///
    /// // Напечатать только токены:
    /// rasm.debug(RASMDebug { stage: RASMDebugStagies::Tokens, until_stage: false });
    ///
    /// // Напечатать токены -> AST -> байты:
    /// rasm.debug(RASMDebug { stage: RASMDebugStagies::Bytes, until_stage: true });
    /// ```
    pub fn debug(&mut self, debug_cond: RASMDebug) {
        // Определяем номер финальной стадии
        let target = match debug_cond.stage {
            RASMDebugStagies::Tokens => 0,
            RASMDebugStagies::AST => 1,
            RASMDebugStagies::Bytes => 2,
        };

        // Стадия 0: токены
        // Всегда нужна — она заполняет self.tokens для следующих стадий.
        let tokens_result = self.tokenize();
        if target == 0 || debug_cond.until_stage {
            match &tokens_result {
                Ok(tokens) => eprintln!("==== Tokens ====\n{tokens:#?}"),
                Err(errs) => {
                    eprintln!("==== Tokens [ERRORS] ====");
                    for e in errs {
                        e.report("(debug)");
                    }
                }
            }
        }
        // Если токенизация провалилась — дальше идти бессмысленно
        if tokens_result.is_err() {
            return;
        }
        if target == 0 {
            return;
        }

        // Стадия 1: AST
        let ast_result = self.parse();
        if target == 1 || debug_cond.until_stage {
            match &ast_result {
                Ok(ast) => eprintln!("==== AST ====\n{ast:#?}"),
                Err(errs) => {
                    eprintln!("==== AST [ERRORS] ====");
                    for e in errs {
                        e.report("(debug)");
                    }
                }
            }
        }
        if ast_result.is_err() {
            return;
        }
        if target == 1 {
            return;
        }

        let bytes_result = self.compile();
        if target == 2 || debug_cond.until_stage {
            match &bytes_result {
                Ok(out) => {
                    eprintln!("==== Bytes ({} bytes) ====", out.code.len());
                    // Hex-дамп: 16 байт в строке
                    for (i, chunk) in out.code.chunks(16).enumerate() {
                        eprint!("{:08x}  ", i * 16);
                        for b in chunk {
                            eprint!("{b:02x} ");
                        }
                        eprintln!();
                    }
                    if !out.label_offsets.is_empty() {
                        eprintln!("==== Labels ====");
                        let mut labels: Vec<_> = out.label_offsets.iter().collect();
                        labels.sort_by_key(|&(_, &off)| off);
                        for (name, offset) in labels {
                            eprintln!("  {name:20} @ 0x{offset:08x}");
                        }
                    }
                }
                Err(errs) => {
                    eprintln!("==== Bytes [ERRORS] ====");
                    for e in errs {
                        e.report("(debug)");
                    }
                }
            }
        }
    }
}