use crate::compiler::{compile_for_eval, compile_for_eval_with_natives};
use crate::lexer::Token;
use crate::parser::{ParseError, Parser};
use crate::vm::{NativeFn, VM, Value, eval_code_with_vm};
use logos::Logos;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::fs;
use std::path::Path;
use std::rc::Rc;
pub type Result<T> = std::result::Result<T, FinxError>;
#[derive(Debug)]
pub enum FinxError {
LexError(String),
ParseError(ParseError),
RuntimeError(String),
IoError(std::io::Error),
NativeFunctionError(String),
CompilerError(String),
VmError(String),
TypeError(String),
UndefinedVariable(String),
}
impl fmt::Display for FinxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FinxError::LexError(msg) => write!(f, "Lexical error: {}", msg),
FinxError::ParseError(err) => write!(f, "Parse error: {}", err),
FinxError::RuntimeError(msg) => write!(f, "Runtime error: {}", msg),
FinxError::IoError(err) => write!(f, "IO error: {}", err),
FinxError::NativeFunctionError(msg) => write!(f, "Native function error: {}", msg),
FinxError::CompilerError(msg) => write!(f, "Compiler error: {}", msg),
FinxError::VmError(msg) => write!(f, "VM error: {}", msg),
FinxError::TypeError(msg) => write!(f, "Type error: {}", msg),
FinxError::UndefinedVariable(msg) => write!(f, "Undefined variable: {}", msg),
}
}
}
impl std::error::Error for FinxError {}
impl From<ParseError> for FinxError {
fn from(err: ParseError) -> Self {
FinxError::ParseError(err)
}
}
impl From<std::io::Error> for FinxError {
fn from(err: std::io::Error) -> Self {
FinxError::IoError(err)
}
}
pub struct Finx {
vm: VM,
native_functions: HashMap<String, (NativeFn, usize)>,
globals_initialized: bool,
known_globals: HashMap<String, usize>,
next_global: usize,
print_output: Rc<RefCell<Vec<String>>>,
}
impl Finx {
pub fn new() -> Self {
let mut engine = Self {
vm: VM::new(),
native_functions: HashMap::new(),
globals_initialized: false,
known_globals: HashMap::new(),
next_global: 0,
print_output: Rc::new(RefCell::new(Vec::new())),
};
engine.register_default_functions();
engine
}
pub fn without_defaults() -> Self {
Self {
vm: VM::new(),
native_functions: HashMap::new(),
globals_initialized: false,
known_globals: HashMap::new(),
next_global: 0,
print_output: Rc::new(RefCell::new(Vec::new())),
}
}
pub fn register_function(&mut self, name: &str, func: NativeFn, num_params: usize) {
self.native_functions
.insert(name.to_string(), (func.clone(), num_params));
self.vm.register_native_function(name, func, num_params);
if !self.known_globals.contains_key(name) {
self.known_globals
.insert(name.to_string(), self.next_global);
self.next_global += 1;
}
self.globals_initialized = false; }
pub fn register_function_ptr(
&mut self,
name: &str,
func: fn(&[Value]) -> Value,
num_params: usize,
) {
let func_rc = Rc::new(func);
self.register_function(name, func_rc, num_params);
}
pub fn register_closure<F>(&mut self, name: &str, closure: F, num_params: usize)
where
F: Fn(&[Value]) -> Value + 'static,
{
let func_rc = Rc::new(closure);
self.register_function(name, func_rc, num_params);
}
pub fn register_functions(&mut self, functions: &[(&str, NativeFn, usize)]) {
for (name, func, num_params) in functions {
self.register_function(name, func.clone(), *num_params);
}
}
pub fn eval(&mut self, source: &str) -> Result<Value> {
let tokens = self.tokenize(source)?;
let ast = self.parse(&tokens)?;
let instructions = self.compile(ast)?;
self.ensure_globals_initialized();
eval_code_with_vm(&mut self.vm, instructions)
}
pub fn eval_file<P: AsRef<Path>>(&mut self, path: P) -> Result<Value> {
let source = fs::read_to_string(path)?;
self.eval(&source)
}
pub fn execute(&mut self, source: &str) -> Result<()> {
self.eval(source)?;
Ok(())
}
pub fn execute_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
self.eval_file(path)?;
Ok(())
}
pub fn get_output(&self) -> Vec<String> {
self.print_output.borrow().clone()
}
pub fn clear_output(&mut self) {
self.print_output.borrow_mut().clear();
}
pub fn set_max_recursion_depth(&mut self, depth: usize) {
self.vm.set_max_recursion_depth(depth);
}
pub fn get_native_function_names(&self) -> Vec<String> {
self.native_functions.keys().cloned().collect()
}
fn tokenize(&self, source: &str) -> Result<Vec<Token>> {
let tokens: std::result::Result<Vec<Token>, _> = Token::lexer(source).collect();
match tokens {
Ok(tokens) => {
let clean_tokens: Vec<Token> = tokens
.into_iter()
.filter(|t| !matches!(t, Token::Error))
.collect();
Ok(clean_tokens)
}
Err(_) => Err(FinxError::LexError("Failed to tokenize source".to_string())),
}
}
fn parse(&self, tokens: &[Token]) -> Result<Vec<crate::parser::Stmt>> {
let mut parser = Parser::new(tokens);
parser.parse().map_err(FinxError::from)
}
fn compile(&mut self, ast: Vec<crate::parser::Stmt>) -> Result<Vec<crate::vm::Instruction>> {
self.extract_global_names(&ast);
let mut all_globals: Vec<(String, usize)> = self
.known_globals
.iter()
.map(|(name, &index)| (name.clone(), index))
.collect();
all_globals.sort_by_key(|(_, index)| *index);
let sorted_global_names: Vec<String> =
all_globals.into_iter().map(|(name, _)| name).collect();
if sorted_global_names.is_empty() {
compile_for_eval(ast)
} else {
compile_for_eval_with_natives(ast, &sorted_global_names)
}
}
fn ensure_globals_initialized(&mut self) {
if !self.globals_initialized {
let max_global_index = self.known_globals.values().max().copied().unwrap_or(0);
self.vm.ensure_globals_capacity(max_global_index + 1); for (name, &index) in &self.known_globals {
if let Some((native_func, num_params)) = self.native_functions.get(name) {
let native_function = crate::vm::NativeFunction {
func: native_func.clone(),
name: name.clone(),
num_params: *num_params,
};
self.vm
.set_global_at_index(index, Value::_NativeFunction(native_function));
}
}
self.globals_initialized = true;
}
}
fn extract_global_names(&mut self, stmts: &[crate::parser::Stmt]) {
for stmt in stmts {
match stmt {
crate::parser::Stmt::Fn { name, .. } => {
if !self.known_globals.contains_key(name) {
self.known_globals.insert(name.clone(), self.next_global);
self.next_global += 1;
}
}
crate::parser::Stmt::Let { name, .. } => {
if !self.known_globals.contains_key(name) {
self.known_globals.insert(name.clone(), self.next_global);
self.next_global += 1;
}
}
crate::parser::Stmt::If {
then_branch,
else_branch,
..
} => {
self.extract_global_names(then_branch);
if let Some(else_stmt) = else_branch {
match else_stmt.as_ref() {
crate::parser::Stmt::If { .. } => {
self.extract_global_names(&[*else_stmt.clone()])
}
_ => {} }
}
}
crate::parser::Stmt::While { body, .. } => {
self.extract_global_names(body);
}
crate::parser::Stmt::For { body, .. } => {
self.extract_global_names(body);
}
_ => {} }
}
}
fn register_default_functions(&mut self) {
self.register_function(
"abs",
Rc::new(|args| {
if let [Value::Number(n)] = args {
Value::Number(n.abs())
} else {
Value::Null }
}),
1,
);
let print_output = self.print_output.clone();
self.register_function(
"print",
Rc::new(move |args| {
let string = format!(
"{}",
args.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(" ")
);
println!("{}", string);
print_output.borrow_mut().push(string);
Value::Null
}),
1,
);
self.register_function(
"sqrt",
Rc::new(|args| {
if let [Value::Number(n)] = args {
Value::Number(n.sqrt())
} else {
Value::Null
}
}),
1,
);
self.register_function(
"min",
Rc::new(|args| {
if let [Value::Number(a), Value::Number(b)] = args {
Value::Number(a.min(*b))
} else {
Value::Null
}
}),
2,
);
self.register_function(
"max",
Rc::new(|args| {
if let [Value::Number(a), Value::Number(b)] = args {
Value::Number(a.max(*b))
} else {
Value::Null
}
}),
2,
);
self.register_function(
"pow",
Rc::new(|args| {
if let [Value::Number(base), Value::Number(exp)] = args {
Value::Number(base.powf(*exp))
} else {
Value::Null
}
}),
2,
);
self.register_function(
"len",
Rc::new(|args| match args {
[Value::Str(s)] => Value::Number(s.len() as f64),
_ => Value::Null,
}),
1,
);
self.register_function(
"is_num",
Rc::new(|args| {
if args.len() == 1 {
Value::Bool(matches!(args[0], Value::Number(_)))
} else {
Value::Null
}
}),
1,
);
self.register_function(
"is_str",
Rc::new(|args| {
if args.len() == 1 {
Value::Bool(matches!(args[0], Value::Str(_)))
} else {
Value::Null
}
}),
1,
);
self.register_function(
"is_bool",
Rc::new(|args| {
if args.len() == 1 {
Value::Bool(matches!(args[0], Value::Bool(_)))
} else {
Value::Null
}
}),
1,
);
self.register_function(
"is_null",
Rc::new(|args| {
if args.len() == 1 {
Value::Bool(matches!(args[0], Value::Null))
} else {
Value::Null
}
}),
1,
);
}
}
impl Default for Finx {
fn default() -> Self {
Self::new()
}
}
#[macro_export]
macro_rules! register_function {
($engine:expr, $name:expr, 1, |$param:ident: f64| -> $return_type:ty $body:block) => {
$engine.register_function(
$name,
std::rc::Rc::new(|args| {
if args.len() != 1 {
return $crate::vm::Value::Null;
}
match &args[0] {
$crate::vm::Value::Number(n) => {
let $param = *n;
let result: $return_type = $body;
result.into()
}
_ => $crate::vm::Value::Null,
}
}),
1,
);
};
($engine:expr, $name:expr, 1, |$param:ident: &str| -> $return_type:ty $body:block) => {
$engine.register_function(
$name,
std::rc::Rc::new(|args| {
if args.len() != 1 {
return $crate::vm::Value::Null;
}
match &args[0] {
$crate::vm::Value::Str(s) => {
let $param = s.as_str();
let result: $return_type = $body;
result.into()
}
_ => $crate::vm::Value::Null,
}
}),
1,
);
};
($engine:expr, $name:expr, 1, |$param:ident: bool| -> $return_type:ty $body:block) => {
$engine.register_function(
$name,
std::rc::Rc::new(|args| {
if args.len() != 1 {
return $crate::vm::Value::Null;
}
match &args[0] {
$crate::vm::Value::Bool(b) => {
let $param = *b;
let result: $return_type = $body;
result.into()
}
_ => $crate::vm::Value::Null,
}
}),
1,
);
};
($engine:expr, $name:expr, 2, |$param1:ident: f64, $param2:ident: f64| -> $return_type:ty $body:block) => {
$engine.register_function(
$name,
std::rc::Rc::new(|args| {
if args.len() != 2 {
return $crate::vm::Value::Null;
}
match (&args[0], &args[1]) {
($crate::vm::Value::Number(n1), $crate::vm::Value::Number(n2)) => {
let $param1 = *n1;
let $param2 = *n2;
let result: $return_type = $body;
result.into()
}
_ => $crate::vm::Value::Null,
}
}),
2,
);
};
($engine:expr, $name:expr, 2, |$param1:ident: f64, $param2:ident: &str| -> $return_type:ty $body:block) => {
$engine.register_function(
$name,
std::rc::Rc::new(|args| {
if args.len() != 2 {
return $crate::vm::Value::Null;
}
match (&args[0], &args[1]) {
($crate::vm::Value::Number(n), $crate::vm::Value::Str(s)) => {
let $param1 = *n;
let $param2 = s.as_str();
let result: $return_type = $body;
result.into()
}
_ => $crate::vm::Value::Null,
}
}),
2,
);
};
($engine:expr, $name:expr, 2, |$param1:ident: &str, $param2:ident: f64| -> $return_type:ty $body:block) => {
$engine.register_function(
$name,
std::rc::Rc::new(|args| {
if args.len() != 2 {
return $crate::vm::Value::Null;
}
match (&args[0], &args[1]) {
($crate::vm::Value::Str(s), $crate::vm::Value::Number(n)) => {
let $param1 = s.as_str();
let $param2 = *n;
let result: $return_type = $body;
result.into()
}
_ => $crate::vm::Value::Null,
}
}),
2,
);
};
($engine:expr, $name:expr, 2, |$param1:ident: &str, $param2:ident: &str| -> $return_type:ty $body:block) => {
$engine.register_function(
$name,
std::rc::Rc::new(|args| {
if args.len() != 2 {
return $crate::vm::Value::Null;
}
match (&args[0], &args[1]) {
($crate::vm::Value::Str(s1), $crate::vm::Value::Str(s2)) => {
let $param1 = s1.as_str();
let $param2 = s2.as_str();
let result: $return_type = $body;
result.into()
}
_ => $crate::vm::Value::Null,
}
}),
2,
);
};
}