mirror of
https://github.com/teo3300/rust-mal.git
synced 2026-01-12 09:15:32 +01:00
17
src/core.rs
17
src/core.rs
@ -35,25 +35,16 @@ pub fn call_func(func: &MalType, args: &[MalType]) -> MalRet {
|
||||
}
|
||||
}
|
||||
|
||||
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])?;
|
||||
let mut left = args[0].if_number()?;
|
||||
if args.len() > 1 {
|
||||
let right = &args[1..];
|
||||
for el in right {
|
||||
left = f(left, if_number(el)?);
|
||||
left = f(left, el.if_number()?);
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,9 +55,9 @@ use MalType::{Bool, Nil};
|
||||
|
||||
pub fn comparison_op(f: fn(isize, isize) -> bool, args: &[MalType]) -> MalRet {
|
||||
let (left, rights) = car_cdr(args)?;
|
||||
let mut left = if_number(left)?;
|
||||
let mut left = left.if_number()?;
|
||||
for right in rights {
|
||||
let right = if_number(right)?;
|
||||
let right = right.if_number()?;
|
||||
if !f(left, right) {
|
||||
return Ok(Nil);
|
||||
}
|
||||
|
||||
35
src/env.rs
35
src/env.rs
@ -1,5 +1,5 @@
|
||||
use crate::core::{arithmetic_op, comparison_op, core_exit};
|
||||
use crate::types::{MalErr, MalType::*};
|
||||
use crate::types::MalErr;
|
||||
use crate::types::{MalMap, MalRet, MalType};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
@ -54,7 +54,7 @@ pub fn env_set(env: &Env, sym: &str, val: &MalType) -> MalType {
|
||||
val.clone()
|
||||
}
|
||||
|
||||
pub fn env_get(env: &Env, sym: &String) -> MalRet {
|
||||
pub fn env_get(env: &Env, sym: &str) -> MalRet {
|
||||
match env.data.borrow().get(sym) {
|
||||
Some(val) => Ok(val.clone()),
|
||||
None => match env.outer.clone() {
|
||||
@ -66,32 +66,17 @@ pub fn env_get(env: &Env, sym: &String) -> MalRet {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::printer::prt;
|
||||
|
||||
pub fn env_binds(outer: Env, binds: &MalType, exprs: &[MalType]) -> Result<Env, MalErr> {
|
||||
let env = env_new(Some(outer));
|
||||
match binds {
|
||||
List(binds) => {
|
||||
if binds.len() != exprs.len() {
|
||||
return Err(MalErr::unrecoverable("Env init with unmatched length"));
|
||||
} // TODO: May be possible to leave this be and not set additional elements at all
|
||||
for (bind, expr) in binds.iter().zip(exprs.iter()) {
|
||||
match bind {
|
||||
Sym(sym) => {
|
||||
env_set(&env, sym, expr);
|
||||
}
|
||||
_ => {
|
||||
return Err(MalErr::unrecoverable(
|
||||
format!("Initializing environment: {:?} is not a symbol", prt(bind))
|
||||
.as_str(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(env)
|
||||
}
|
||||
_ => Err(MalErr::unrecoverable("init: first argument must be a list")),
|
||||
let binds = binds.if_list()?;
|
||||
if binds.len() != exprs.len() {
|
||||
return Err(MalErr::unrecoverable("Wrong number of arguments"));
|
||||
} // TODO: May be possible to leave this be and not set additional elements at all
|
||||
for (bind, expr) in binds.iter().zip(exprs.iter()) {
|
||||
let bind = bind.if_symbol()?;
|
||||
env_set(&env, bind, expr);
|
||||
}
|
||||
Ok(env)
|
||||
}
|
||||
|
||||
use crate::types::MalType::{Fun, Str};
|
||||
|
||||
73
src/eval.rs
73
src/eval.rs
@ -1,6 +1,6 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::core::{call_func, car_cdr, scream};
|
||||
use crate::core::{call_func, car_cdr};
|
||||
use crate::env::Env;
|
||||
use crate::env::{env_get, env_new, env_set};
|
||||
use crate::printer::prt;
|
||||
@ -10,13 +10,9 @@ use crate::types::{MalArgs, MalErr, MalMap, MalRet, MalType};
|
||||
/// Resolve the first element of the list as the function name and call it
|
||||
/// with the other elements as arguments
|
||||
fn eval_func(list: &MalType) -> MalRet {
|
||||
match list {
|
||||
List(list) => {
|
||||
let (func, args) = car_cdr(list)?;
|
||||
call_func(func, args)
|
||||
}
|
||||
_ => scream(),
|
||||
}
|
||||
let list = list.if_list()?;
|
||||
let (func, args) = car_cdr(list)?;
|
||||
call_func(func, args)
|
||||
}
|
||||
|
||||
// When evaluating an expression it's possible
|
||||
@ -35,17 +31,9 @@ fn def_bang_form(list: &[MalType], env: Env) -> MalRet {
|
||||
if list.len() != 2 {
|
||||
return Err(MalErr::unrecoverable("def! form: needs 2 arguments"));
|
||||
}
|
||||
match &list[0] {
|
||||
Sym(sym) => Ok(env_set(&env, sym.as_str(), &eval(&list[1], env.clone())?)),
|
||||
_ => Err(MalErr::unrecoverable(
|
||||
format!(
|
||||
"def! Assigning {:?} to {:?}, which is not a symbol",
|
||||
prt(&list[1]),
|
||||
prt(&list[0])
|
||||
)
|
||||
.as_str(),
|
||||
)),
|
||||
}
|
||||
let (car, _) = car_cdr(list)?;
|
||||
let sym = car.if_symbol()?;
|
||||
Ok(env_set(&env, sym, &eval(&list[1], env.clone())?))
|
||||
}
|
||||
|
||||
/// let* special form:
|
||||
@ -56,22 +44,20 @@ fn let_star_form(list: &[MalType], env: Env) -> MalRet {
|
||||
let inner_env = env_new(Some(env.clone()));
|
||||
// change the inner environment
|
||||
let (car, cdr) = car_cdr(list)?;
|
||||
match car {
|
||||
List(list) if list.len() % 2 == 0 => {
|
||||
// TODO: Find a way to avoid index looping that is ugly
|
||||
for i in (0..list.len()).step_by(2) {
|
||||
def_bang_form(&list[i..=i + 1], inner_env.clone())?;
|
||||
}
|
||||
if cdr.is_empty() {
|
||||
// TODO: check if it exists a better way to do this
|
||||
Ok(Nil)
|
||||
} else {
|
||||
eval(&cdr[0], inner_env)
|
||||
}
|
||||
let list = car.if_list()?;
|
||||
if list.len() % 2 == 0 {
|
||||
// TODO: Find a way to avoid index looping that is ugly
|
||||
for i in (0..list.len()).step_by(2) {
|
||||
def_bang_form(&list[i..=i + 1], inner_env.clone())?;
|
||||
}
|
||||
_ => Err(MalErr::unrecoverable(
|
||||
"First argument of let* must be a list of pair definitions",
|
||||
)),
|
||||
if cdr.is_empty() {
|
||||
// TODO: check if it exists a better way to do this
|
||||
Ok(Nil)
|
||||
} else {
|
||||
eval(&cdr[0], inner_env)
|
||||
}
|
||||
} else {
|
||||
Err(MalErr::unrecoverable("let* form, number of arguments must be even"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,20 +104,13 @@ use crate::printer::print_malfun;
|
||||
|
||||
pub fn help_form(list: &[MalType], env: Env) -> MalRet {
|
||||
let (sym, _) = car_cdr(list)?;
|
||||
match car_cdr(list)?.0 {
|
||||
Sym(sym_str) => {
|
||||
match eval(sym, env.clone())? {
|
||||
Fun(_, desc) => println!("{}\t[builtin]: {}", sym_str, desc),
|
||||
MalFun { params, ast, .. } => print_malfun(sym_str, params, ast),
|
||||
_ => println!("{}\t[symbol]: {}", sym_str, prt(&env_get(&env, sym_str)?)),
|
||||
}
|
||||
Ok(Bool(true))
|
||||
}
|
||||
_ => {
|
||||
println!("{} is not a symbol", prt(sym));
|
||||
Ok(Nil)
|
||||
}
|
||||
let sym_str = sym.if_symbol()?;
|
||||
match eval(sym, env.clone())? {
|
||||
Fun(_, desc) => println!("{}\t[builtin]: {}", sym_str, desc),
|
||||
MalFun { params, ast, .. } => print_malfun(sym_str, params, ast),
|
||||
_ => println!("{}\t[symbol]: {}", sym_str, prt(&env_get(&env, sym_str)?)),
|
||||
}
|
||||
Ok(Bool(true))
|
||||
}
|
||||
|
||||
/// Intermediate function to discern special forms from defined symbols
|
||||
|
||||
@ -49,7 +49,7 @@ pub fn prt(ast: &MalType) -> String {
|
||||
pr_str(ast, true)
|
||||
}
|
||||
|
||||
pub fn print_malfun(sym: &String, params: Rc<MalType>, ast: Rc<MalType>) {
|
||||
pub fn print_malfun(sym: &str, params: Rc<MalType>, ast: Rc<MalType>) {
|
||||
println!("{}\t[function]: {}", sym, prt(¶ms));
|
||||
match ast.as_ref() {
|
||||
List(list) => {
|
||||
|
||||
25
src/types.rs
25
src/types.rs
@ -23,6 +23,31 @@ pub enum MalType {
|
||||
Nil,
|
||||
}
|
||||
|
||||
impl MalType {
|
||||
pub fn if_number(&self) -> Result<isize, MalErr> {
|
||||
match self {
|
||||
Self::Int(val) => Ok(*val),
|
||||
_ => Err(MalErr::unrecoverable(
|
||||
format!("{:?} is not a number", prt(self)).as_str(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn if_list(&self) -> Result<&[MalType], MalErr> {
|
||||
match self {
|
||||
Self::List(list) => Ok(list),
|
||||
_ => Err(MalErr::unrecoverable(format!("{:?} is not a list", prt(self)).as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn if_symbol(&self) -> Result<&str, MalErr> {
|
||||
match self {
|
||||
Self::Sym(sym) => Ok(sym),
|
||||
_ => Err(MalErr::unrecoverable(format!("{:?} is not a symbol", prt(self)).as_str()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum Severity {
|
||||
Recoverable,
|
||||
|
||||
Reference in New Issue
Block a user