Eval cycle implemented

Yet to be implement:
- Environment
- Function calls

Signed-off-by: teo3300 <matteo.rogora@live.it>
This commit is contained in:
teo3300
2023-06-10 23:19:29 +02:00
parent 7be624e032
commit fe6fbf061c
4 changed files with 136 additions and 20 deletions

23
src/envs.rs Normal file
View File

@ -0,0 +1,23 @@
use std::collections::HashMap;
use crate::types::{KeyType, MalRet, MalType};
pub struct Env {
map: HashMap<String, MalType>,
}
impl Env {
pub fn new() -> Self {
Env {
map: HashMap::new(),
}
}
pub fn solve(&self, sym: KeyType) -> MalRet {
let v = sym.val;
match self.map.get(&v) {
Some(val) => Ok(val.clone()),
None => Err(format!("symbol {:?} not defined", v)),
}
}
}

View File

@ -1,15 +1,21 @@
// io lib to read input and print output
use std::io::{self, Write};
mod envs;
mod mal_core;
mod printer;
mod reader;
mod types;
mod step1_read_print;
use step1_read_print::rep;
use envs::Env;
fn main() -> io::Result<()> {
mod step2_eval;
use step2_eval::rep;
fn main() {
let mut num = 0;
let env = Env::new();
loop {
let mut input = String::new();
loop {
@ -19,28 +25,23 @@ fn main() -> io::Result<()> {
// Read line to compose program inpug
let mut line = String::new();
io::stdin().read_line(&mut line)?;
io::stdin().read_line(&mut line).unwrap();
input.push_str(&line);
if input == "\n" {
break;
}
if input != "\n" {
// Perform rep on whole available input
match rep(&input) {
Ok(output) => {
num += 1;
println!("[{}]> {}", num, output);
}
match rep(&input, &env) {
Ok(output) => println!("[{}]> {}", num, output),
Err(err) => {
if line == "\n" {
num += 1;
println!("; [{}]> {}", num, err);
} else {
if line != "\n" {
continue;
}
println!("; [{}]> Error {}", num, err);
}
};
}
}
num += 1;
break;
}
}

51
src/mal_core.rs Normal file
View File

@ -0,0 +1,51 @@
use crate::envs::Env;
use crate::types::MalType::*;
use crate::types::{MalRet, MalType};
fn function_call(list: MalType) -> MalRet {
match list {
List(list) => {
let _func = &list[0];
if list.len() > 1 {
let _ast = &list[1..list.len() - 1];
todo!("call: func(args)");
} else {
todo!("call: func()");
}
}
_ => Err("YOU SHOULD NOT BE HERE".to_string()),
}
}
pub fn eval(ast: MalType, env: &Env) -> MalRet {
match &ast {
List(list) => {
if list.is_empty() {
Ok(ast)
} else {
let ev = eval_ast(ast, env)?;
function_call(ev)
}
}
_ => eval_ast(ast, env),
}
}
fn eval_list(list: Vec<MalType>, env: &Env) -> MalRet {
let mut ret = Vec::new();
for el in list {
match eval(el, env) {
Ok(val) => ret.push(val),
Err(err) => return Err(err),
}
}
Ok(List(ret))
}
pub fn eval_ast(ast: MalType, env: &Env) -> MalRet {
match ast {
Sym(sym) => env.solve(sym), // resolve
List(list) => eval_list(list, env),
_ => Ok(ast), // Default behavior, do not resolve
}
}

41
src/step2_eval.rs Normal file
View File

@ -0,0 +1,41 @@
// Structure the main functions of the interpreter
//
// For now just act as an echo, note that each function should not modify the
// input, thus this can be referenced by the previous step without the need
// to allocate more memory
use crate::envs::Env;
use crate::mal_core::eval;
use crate::printer::pr_str;
use crate::reader::read_str;
use crate::types::{MalRet, MalType};
#[allow(non_snake_case)]
/// Read input and generate an ast
fn READ(input: &str) -> MalRet {
match read_str(input) {
Ok(ast) => Ok(ast),
Err(err) => Err(format!("@ READ: {}", err)),
}
}
#[allow(non_snake_case)]
/// Evaluate the generated ast
fn EVAL(ast: MalType, env: &Env) -> MalRet {
match eval(ast, env) {
Ok(ast) => Ok(ast),
Err(err) => Err(format!("@ EVAL: {}", err)),
}
}
#[allow(non_snake_case)]
/// Print out the result of the evaluation
fn PRINT(output: MalType) -> String {
pr_str(&output, true)
}
pub fn rep(input: &str, env: &Env) -> Result<String, String> {
let ast = READ(input)?;
let out = EVAL(ast, env)?;
Ok(PRINT(out))
}