<commit_before>use super::{value::Value, Vm};
use crate::{builtin_functions::BuiltinFunction, compiler::lir::Instruction, input::Input};
use itertools::Itertools;
use log::{debug, trace};

impl Vm {
    pub(super) fn run_builtin_function(&mut self, builtin_function: BuiltinFunction) {
        trace!("run_builtin_function: builtin{:?}", builtin_function);

        let return_value = match builtin_function {
            BuiltinFunction::Add => self.add(),
            BuiltinFunction::Equals => Ok(self.equals()),
            BuiltinFunction::GetArgumentCount => self.get_argument_count(),
            BuiltinFunction::IfElse => self.if_else(),
            BuiltinFunction::Panic => self.panic_builtin(),
            BuiltinFunction::Print => self.print(),
            BuiltinFunction::StructGet => self.struct_get(),
            BuiltinFunction::StructGetKeys => self.struct_get_keys(),
            BuiltinFunction::StructHasKey => self.struct_has_key(),
            BuiltinFunction::TypeOf => Ok(self.type_of()),
            BuiltinFunction::Use => self.use_(),
            _ => panic!("Unhandled builtin function: {:?}", builtin_function),
        };
        let return_value = match return_value {
            Ok(value) => value,
            Err(panic_message) => self.panic(panic_message),
        };

        let return_object = self.heap.import(return_value);
        self.data_stack.push(return_object);
    }

    fn add(&mut self) -> Result<Value, String> {
        let b = self
            .pop_value()
            .unwrap()
            .try_into_int()
            .map_err(|it| format!("builtinAdd expects numbers as arguments, got {}.", it))?;
        let a = self
            .pop_value()
            .unwrap()
            .try_into_int()
            .map_err(|it| format!("builtinAdd expects numbers as arguments, got {}.", it))?;

        Ok((a + b).into())
    }

    fn equals(&mut self) -> Value {
        let b = self.pop_value().unwrap();
        let a = self.pop_value().unwrap();

        (a == b).into()
    }

    fn get_argument_count(&mut self) -> Result<Value, String> {
        let (_, chunk) = self
            .pop_value()
            .unwrap()
            .try_into_closure()
            .map_err(|it| format!("builtinAddArgumentCount expects a closure, got {}.", it))?;

        let num_args = self.chunks[chunk].num_args;
        Ok(Value::Int(num_args as u64))
    }

    fn if_else(&mut self) -> Result<Value, String> {
        let else_ = self.pop_value().unwrap();
        let then = self.pop_value().unwrap();
        let condition = self.pop_value().unwrap().try_into_symbol().map_err(|it| {
            format!(
                "builtinIfElse expects a boolean symbol as the condition, got {}.",
                it
            )
        })?;
        match then.clone() {
            Value::Closure { body, .. } => {
                if self.chunks[body].num_args > 0 {
                    return Err(format!("builtinIfElse expects a closure without arguments as the then, got one with {} arguments.", self.chunks[body].num_args));
                }
            }
            _ => {
                return Err(format!(
                    "builtinIfElse expects a closure as the then, got {}.",
                    then
                ))
            }
        };
        match else_.clone() {
            Value::Closure { body, .. } => {
                if self.chunks[body].num_args > 0 {
                    return Err(format!("builtinIfElse expects a closure without arguments as the else, got one with {} arguments.", self.chunks[body].num_args));
                }
            }
            _ => {
                return Err(format!(
                    "builtinIfElse expects a closure as the else, got {}.",
                    else_
                ))
            }
        };
        let condition = match condition.as_str() {
            "True" => true,
            "False" => false,
            _ => {
                return Err(format!(
                    "builtinIfElse expected True or False as a condition, but got {}",
                    condition
                ));
            }
        };

        let closure_object = self.heap.import(if condition { then } else { else_ });
        self.data_stack.push(closure_object);
        self.run_instruction(Instruction::Call);
        Ok(self.pop_value().unwrap())
    }

    fn panic_builtin(&mut self) -> Result<Value, String> {
        let message = self
            .pop_value()
            .unwrap()
            .try_into_text()
            .map_err(|it| format!("builtinPanic expects a text as the message, got {}.", it))?;

        Ok(self.panic(message))
    }

    fn print(&mut self) -> Result<Value, String> {
        let message = self
            .pop_value()
            .unwrap()
            .try_into_text()
            .map_err(|it| format!("builtinPrint expects a text as the message, got {}.", it))?;

        println!("{:?}", message);<commit_msg>Special-case builtinIfElse for control flow<commit_after>use super::{value::Value, Vm};
use crate::{builtin_functions::BuiltinFunction, compiler::lir::Instruction, input::Input};
use itertools::Itertools;
use log::{debug, trace};

impl Vm {
    pub(super) fn run_builtin_function(&mut self, builtin_function: BuiltinFunction) {
        trace!("run_builtin_function: builtin{:?}", builtin_function);

        let return_value_or_panic_message = match builtin_function {
            BuiltinFunction::Add => self.add(),
            BuiltinFunction::Equals => Ok(self.equals()),
            BuiltinFunction::GetArgumentCount => self.get_argument_count(),
            BuiltinFunction::IfElse => match self.if_else() {
                // If successful, builtinIfElse doesn't return a value, but
                // diverges the control flow.
                Ok(()) => return,
                Err(message) => Err(message),
            },
            BuiltinFunction::Panic => self.panic_builtin(),
            BuiltinFunction::Print => self.print(),
            BuiltinFunction::StructGet => self.struct_get(),
            BuiltinFunction::StructGetKeys => self.struct_get_keys(),
            BuiltinFunction::StructHasKey => self.struct_has_key(),
            BuiltinFunction::TypeOf => Ok(self.type_of()),
            BuiltinFunction::Use => self.use_(),
        };
        let return_value = match return_value_or_panic_message {
            Ok(value) => value,
            Err(panic_message) => self.panic(panic_message),
        };

        let return_object = self.heap.import(return_value);
        self.data_stack.push(return_object);
    }

    fn add(&mut self) -> Result<Value, String> {
        let b = self
            .pop_value()
            .unwrap()
            .try_into_int()
            .map_err(|it| format!("builtinAdd expects numbers as arguments, got {}.", it))?;
        let a = self
            .pop_value()
            .unwrap()
            .try_into_int()
            .map_err(|it| format!("builtinAdd expects numbers as arguments, got {}.", it))?;

        Ok((a + b).into())
    }

    fn equals(&mut self) -> Value {
        let b = self.pop_value().unwrap();
        let a = self.pop_value().unwrap();

        (a == b).into()
    }

    fn get_argument_count(&mut self) -> Result<Value, String> {
        let (_, chunk) = self
            .pop_value()
            .unwrap()
            .try_into_closure()
            .map_err(|it| format!("builtinAddArgumentCount expects a closure, got {}.", it))?;

        let num_args = self.chunks[chunk].num_args;
        Ok(Value::Int(num_args as u64))
    }

    fn if_else(&mut self) -> Result<(), String> {
        let else_ = self.pop_value().unwrap();
        let then = self.pop_value().unwrap();
        let condition = self.pop_value().unwrap().try_into_symbol().map_err(|it| {
            format!(
                "builtinIfElse expects a boolean symbol as the condition, got {}.",
                it
            )
        })?;
        match then.clone() {
            Value::Closure { body, .. } => {
                if self.chunks[body].num_args > 0 {
                    return Err(format!("builtinIfElse expects a closure without arguments as the then, got one with {} arguments.", self.chunks[body].num_args));
                }
            }
            _ => {
                return Err(format!(
                    "builtinIfElse expects a closure as the then, got {}.",
                    then
                ))
            }
        };
        match else_.clone() {
            Value::Closure { body, .. } => {
                if self.chunks[body].num_args > 0 {
                    return Err(format!("builtinIfElse expects a closure without arguments as the else, got one with {} arguments.", self.chunks[body].num_args));
                }
            }
            _ => {
                return Err(format!(
                    "builtinIfElse expects a closure as the else, got {}.",
                    else_
                ))
            }
        };
        let condition = match condition.as_str() {
            "True" => true,
            "False" => false,
            _ => {
                return Err(format!(
                    "builtinIfElse expected True or False as a condition, but got {}",
                    condition
                ));
            }
        };

        let closure_object = self.heap.import(if condition { then } else { else_ });
        debug!(
            "IfElse executing the closure: {:?}",
            self.heap.export_without_dropping(closure_object)
        );
        self.data_stack.push(closure_object);
        self.run_instruction(Instruction::Call);
        Ok(())
    }

    fn panic_builtin(&mut self) -> Result<Value, String> {
        let message = self
            .pop_value()
            .unwrap()
            .try_into_text()
            .map_err(|it| format!("builtinPanic expects a text as the message, got {}.", it))?;

        Ok(self.panic(message))
    }

    fn print(&mut self) -> Result<Value, String> {
        let message = self
            .pop_value()
            .unwrap()
            .try_into_text()
            .map_err(|it| format!("builtinPrint expects a text as the message, got {}.", it))?;

        println!("{:?}", message);
{'fraction_non_alphanumeric': 0.13271665043816944, 'fraction_numerical': 0.0007789678675754625, 'mean_word_length': 1.779702300405954, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 72, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3501527', 'n_tokens_mistral': 2838, 'n_tokens_neox': 2804, 'n_words': 628}