use crate::RASM;
use rasm_core::{
codegen::{
CodeGen, CodegenOutput,
aot::{Formats, write_native_executable},
jit::{JitError, JitExecutor, symbol::SymbolTable},
},
error::{Error, ErrorKind},
lexer::Lexer,
parser::Parser,
};
use std::{fmt, path::Path};
#[derive(Debug)]
pub enum RASMRuntimeError<'a> {
Compiler(Vec<rasm_core::error::Error<'a>>),
NoEntryPoint,
Jit(JitError),
Io(std::io::Error),
}
impl fmt::Display for RASMRuntimeError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Compiler(errors) => write!(f, "compiler failed with {} error(s)", errors.len()),
Self::NoEntryPoint => write!(f, "no entry point found; define '_start' or 'main'"),
Self::Jit(err) => write!(f, "{err}"),
Self::Io(err) => write!(f, "{err}"),
}
}
}
impl std::error::Error for RASMRuntimeError<'_> {}
impl<'a> From<Vec<rasm_core::error::Error<'a>>> for RASMRuntimeError<'a> {
fn from(errors: Vec<rasm_core::error::Error<'a>>) -> Self {
Self::Compiler(errors)
}
}
impl From<JitError> for RASMRuntimeError<'_> {
fn from(err: JitError) -> Self {
Self::Jit(err)
}
}
impl From<std::io::Error> for RASMRuntimeError<'_> {
fn from(err: std::io::Error) -> Self {
Self::Io(err)
}
}
impl RASM {
pub fn assemble(&mut self) -> Result<CodegenOutput, Vec<Error<'_>>> {
let mut lexer = Lexer::new(&mut self.str_pool, self.src.clone());
lexer.tokenize();
if !lexer.errors.is_empty() {
return Err(lexer
.errors
.into_iter()
.map(|lexer_err| {
let position = lexer_err.pos;
Error::new(ErrorKind::Lexer(lexer_err), position, &self.src)
})
.collect());
}
self.tokens = lexer.tokens.clone();
let mut parser = Parser::new(&self.tokens, &mut self.str_pool);
let ast = parser.parse();
if !parser.errors.is_empty() {
return Err(parser
.errors
.into_iter()
.map(|parser_err| {
let position = parser_err.pos;
Error::new(ErrorKind::Parser(parser_err), position, &self.src)
})
.collect());
}
self.ast = ast;
let mut codegen = CodeGen::new(&self.str_pool);
let output = codegen.compile(&self.ast);
if !codegen.errors.is_empty() {
return Err(codegen
.errors
.into_iter()
.map(|cg_err| Error::new(ErrorKind::Codegen(cg_err), Default::default(), &self.src))
.collect());
}
Ok(output)
}
pub fn emit_bytes(&mut self) -> Result<Vec<u8>, Vec<Error<'_>>> {
Ok(self.assemble()?.code)
}
pub fn entry_offset(output: &CodegenOutput) -> Option<usize> {
output
.label_offsets
.get("_start")
.or_else(|| output.label_offsets.get("main"))
.copied()
}
pub fn jit_executor(&mut self) -> Result<JitExecutor, RASMRuntimeError<'_>> {
let output = self.assemble().map_err(RASMRuntimeError::Compiler)?;
let entry_offset = Self::entry_offset(&output).ok_or(RASMRuntimeError::NoEntryPoint)?;
let mut symbols = SymbolTable::new();
JitExecutor::new(&output.code, entry_offset, &mut symbols, &[]).map_err(Into::into)
}
pub unsafe fn jit_execute(&mut self) -> Result<i64, RASMRuntimeError<'_>> {
let executor = self.jit_executor()?;
Ok(unsafe { executor.execute() })
}
pub fn write_executable(
&mut self,
path: impl AsRef<Path>,
format: Formats,
) -> Result<(), RASMRuntimeError<'_>> {
let output = self.assemble().map_err(RASMRuntimeError::Compiler)?;
let entry_offset = Self::entry_offset(&output).ok_or(RASMRuntimeError::NoEntryPoint)?;
let path = path.as_ref().to_string_lossy();
write_native_executable(&output.code, entry_offset, &path, format).map_err(Into::into)
}
}