Created core file

- keep core functions in a different file
- I can return custom values UwU

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2023-11-14 20:55:28 +09:00
parent 0cca2f2b9b
commit f240d31f27
6 changed files with 113 additions and 98 deletions

103
src/core.rs Normal file
View File

@ -0,0 +1,103 @@
// This file should contain all the necessary function to define builtin functions
use crate::env::env_binds;
use crate::printer::prt;
use crate::types::{MalType, MalErr, MalRet};
use crate::types::MalType::{Fun, MalFun, List};
use MalType::Int;
pub fn scream() -> MalRet {
panic!("If this messagge occurs, something went terribly wrong")
}
pub fn call_func(func: &MalType, args: &[MalType]) -> MalRet {
match func {
Fun(func, _) => func(args),
MalFun {
eval,
params,
ast,
env,
} => {
let inner_env = env_binds(env.clone(), params, args)?;
// It's fine to clone the environment here
// since this is when the function is actually called
match eval(ast, inner_env)? {
List(list) => Ok(list.last().unwrap_or(&Nil).clone()),
_ => scream(),
}
}
_ => Err(MalErr::unrecoverable(
format!("{:?} is not a function", prt(func)).as_str(),
)),
}
}
fn if_number(val: &MalType) -> Result<isize, MalErr> {
match val {
Int(val) => Ok(*val),
_ => Err(MalErr::unrecoverable(
format!("{:?} is not a number", prt(&val)).as_str(),
)),
}
}
pub fn arithmetic_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType]) -> MalRet {
if args.is_empty() {
return Ok(Int(set));
}
let mut left = if_number(&args[0])?;
if args.len() > 1 {
let right = &args[1..];
for el in right {
left = f(left, if_number(el)?);
}
}
Ok(Int(left))
}
use MalType::{Bool, Nil};
pub fn comparison_op(f: fn(isize, isize) -> bool, args: &[MalType]) -> MalRet {
match args.len() {
0 => Err(MalErr::unrecoverable(
"Comparison requires at least 1 argument",
)),
_ => {
let (left, rights) = car_cdr(args);
let mut left = if_number(left)?;
for right in rights {
let right = if_number(right)?;
if !f(left, right) {
return Ok(Nil);
}
left = right;
}
Ok(Bool(true))
}
}
}
/// Extract the car and cdr from a list
pub fn car_cdr(list: &[MalType]) -> (&MalType, &[MalType]) {
(
&list[0],
if list.len() > 1 {
&list[1..]
} else {
&list[0..0]
},
)
}
use std::process::exit;
pub fn core_exit(list: &[MalType]) -> MalRet {
match car_cdr(list).0 {
Int(val) => exit(*val as i32),
_ => exit(-1)
}
}

View File

@ -1,3 +1,4 @@
use crate::core::{arithmetic_op, comparison_op, core_exit};
use crate::types::{MalErr, MalType::*};
use crate::types::{MalMap, MalRet, MalType};
use std::cell::RefCell;
@ -91,18 +92,11 @@ pub fn env_binds(outer: Env, binds: &MalType, exprs: &[MalType]) -> Result<Env,
}
use crate::types::MalType::{Fun, Str};
use crate::types::{arithmetic_op, comparison_op};
use std::process::exit;
pub fn scream() -> MalRet {
panic!("If this messagge occurs, something went terribly wrong")
}
pub fn env_init() -> Env {
env_init!(None,
"test" => Fun(|_| Ok(Str("This is a test function".to_string())), "Just a test function"),
"quit" => Fun(|_| {exit(0)}, "Quits the program with success status (0)"),
"help" => Fun(|_| {scream()}, "Gets information about the symbols"),
"exit" => Fun(|a| {core_exit(a)}, "Quits the program with specified status"),
"+" => Fun(|a| arithmetic_op(0, |a, b| a + b, a), "Returns the sum of the arguments"),
"-" => Fun(|a| arithmetic_op(0, |a, b| a - b, a), "Returns the difference of the arguments"),
"*" => Fun(|a| arithmetic_op(1, |a, b| a * b, a), "Returns the product of the arguments"),

View File

@ -1,32 +1,9 @@
use crate::env::{env_binds, env_get, env_new, env_set};
use crate::env::{scream, Env};
use crate::env::{env_get, env_new, env_set};
use crate::env::Env;
use crate::printer::prt;
use crate::types::MalType::*;
use crate::types::{car_cdr, MalErr};
use crate::types::{MalArgs, MalMap, MalRet, MalType};
fn call_func(func: &MalType, args: &[MalType]) -> MalRet {
match func {
Fun(func, _) => func(args),
MalFun {
eval,
params,
ast,
env,
} => {
let inner_env = env_binds(env.clone(), params, args)?;
// It's fine to clone the environment here
// since this is when the function is actually called
match eval(ast, inner_env)? {
List(list) => Ok(list.last().unwrap_or(&Nil).clone()),
_ => scream(),
}
}
_ => Err(MalErr::unrecoverable(
format!("{:?} is not a function", prt(func)).as_str(),
)),
}
}
use crate::core::{car_cdr, call_func};
use crate::types::{MalArgs, MalMap, MalRet, MalErr, MalType};
/// Resolve the first element of the list as the function name and call it
/// with the other elements as arguments

View File

@ -8,6 +8,7 @@ mod printer;
mod reader;
mod step4_if_fn_do;
mod types;
mod core;
use env::env_init;
use parse_tools::{load_file, interactive};

View File

@ -107,65 +107,4 @@ pub fn unescape_str(s: &str) -> String {
.replace("\\\\", "\\")
.replace("\\n", "\n")
.replace("\\\"", "\"")
}
/// Extract the car and cdr from a list
pub fn car_cdr(list: &[MalType]) -> (&MalType, &[MalType]) {
(
&list[0],
if list.len() > 1 {
&list[1..]
} else {
&list[0..0]
},
)
}
use MalType::Int;
fn if_number(val: &MalType) -> Result<isize, MalErr> {
match val {
Int(val) => Ok(*val),
_ => Err(MalErr::unrecoverable(
format!("{:?} is not a number", prt(&val)).as_str(),
)),
}
}
pub fn arithmetic_op(set: isize, f: fn(isize, isize) -> isize, args: &[MalType]) -> MalRet {
if args.is_empty() {
return Ok(Int(set));
}
let mut left = if_number(&args[0])?;
if args.len() > 1 {
let right = &args[1..];
for el in right {
left = f(left, if_number(el)?);
}
}
Ok(Int(left))
}
use MalType::{Bool, Nil};
pub fn comparison_op(f: fn(isize, isize) -> bool, args: &[MalType]) -> MalRet {
match args.len() {
0 => Err(MalErr::unrecoverable(
"Comparison requires at least 1 argument",
)),
_ => {
let (left, rights) = car_cdr(args);
let mut left = if_number(left)?;
for right in rights {
let right = if_number(right)?;
if !f(left, right) {
return Ok(Nil);
}
left = right;
}
Ok(Bool(true))
}
}
}
}

View File

@ -8,4 +8,5 @@
(test 1 2 14) (help +)
(help -)(func 1 2 3)
(help test)
; (quit)
; I even implemented return statement
(exit (func 1 2 3))