Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 98 additions & 80 deletions src/builtin.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
// This file should only be used at runtime


use std::io::{BufRead, Write};
use std::rc::Rc;
use log::debug;
use crate::debug_output::build_expr_debug_strings;
use crate::expr::ExprAST;
use crate::interpreter::{GlobalState, IroncamelFileInfo};
use log::debug;
use std::io::{BufRead, Write};
use std::rc::Rc;

pub const IRONCAMEL_BUILTIN_FUNCTIONS: &[&str; 7] = &["cons", "hd", "tl", "list", "is_empty",
"atoi", "strtok"];
pub const ARITHMETIC_OPERATORS: &[&str; 8] = &["<=", ">=", "+", "-", "*", "==", ">", "<", ];
pub const IRONCAMEL_BUILTIN_FUNCTIONS: &[&str; 7] =
&["cons", "hd", "tl", "list", "is_empty", "atoi", "strtok"];
pub const ARITHMETIC_OPERATORS: &[&str; 8] = &["<=", ">=", "+", "-", "*", "==", ">", "<"];
#[allow(dead_code)]
pub const IO_OPERATIONS: &[&str; 5] = &["readstr", "writeline", "writelist", "fopen_read", "fopen_write"];
pub const IO_OPERATIONS: &[&str; 5] = &[
"readstr",
"writeline",
"writelist",
"fopen_read",
"fopen_write",
];

pub(crate) fn perform_read(method_name:&str, file_handler: &str, global_state: &mut GlobalState) -> ExprAST {
pub(crate) fn perform_read(
method_name: &str,
file_handler: &str,
global_state: &mut GlobalState,
) -> ExprAST {
let fop = global_state.open_file_list.get_mut(file_handler).unwrap();
match method_name {
"readstr" => {
Expand All @@ -23,7 +32,9 @@ pub(crate) fn perform_read(method_name:&str, file_handler: &str, global_state: &
IroncamelFileInfo::FileRead(buf) => {
let _ = buf.read_line(&mut s);
}
IroncamelFileInfo::FileWrite(_) | IroncamelFileInfo::Stdout => { panic!() }
IroncamelFileInfo::FileWrite(_) | IroncamelFileInfo::Stdout => {
panic!()
}
IroncamelFileInfo::Stdin => {
let mut t = String::new();
std::io::stdin().read_line(&mut t).unwrap();
Expand All @@ -32,22 +43,27 @@ pub(crate) fn perform_read(method_name:&str, file_handler: &str, global_state: &
};
let expr = ExprAST::StringLiteral(s);
expr
},
_ => panic!("No such write function ({})", method_name)
}
_ => panic!("No such write function ({})", method_name),
}
}

pub fn perform_write(method_name:&str, file_handler: &str, data:&ExprAST, global_state: &mut GlobalState) {
pub fn perform_write(
method_name: &str,
file_handler: &str,
data: &ExprAST,
global_state: &mut GlobalState,
) {
let fop: &mut IroncamelFileInfo = global_state.open_file_list.get_mut(file_handler).unwrap();
match method_name {
"writeline" => writeline(data, fop),
"writelist" => writelist(data, fop),
_ => panic!("No such write function ({})", method_name)
_ => panic!("No such write function ({})", method_name),
}
}

fn writelist(list: &ExprAST, fop: &mut IroncamelFileInfo) {
let mut list = match list {
let mut list = match list {
ExprAST::List(l) => l,
_ => panic!("Expect a list, got {:?}", build_expr_debug_strings(list)),
};
Expand All @@ -56,7 +72,7 @@ fn writelist(list: &ExprAST, fop: &mut IroncamelFileInfo) {
write_internal(" ", fop);
list = &*match &list.next {
Some(l) => l,
None => break
None => break,
}
}
write_internal("\n", fop);
Expand All @@ -66,22 +82,27 @@ fn write_internal(s: &str, fop: &mut IroncamelFileInfo) {
match fop {
IroncamelFileInfo::FileWrite(fs) => {
let _ = fs.write_all(s.as_ref());
},
}
IroncamelFileInfo::Stdout => {
print!("{}", s);
},
IroncamelFileInfo::FileRead(_) | IroncamelFileInfo::Stdin => {panic!()}

}
IroncamelFileInfo::FileRead(_) | IroncamelFileInfo::Stdin => {
panic!()
}
}
}
fn write(e: &ExprAST, fop: &mut IroncamelFileInfo) {
match e {
ExprAST::Int(x) => write_internal(&x.to_string(), fop),
ExprAST::Bool(x) => {
if *x {write_internal("true", fop)} else {write_internal("false", fop)}
if *x {
write_internal("true", fop)
} else {
write_internal("false", fop)
}
}
ExprAST::StringLiteral(s) => write_internal(s, fop),
_ => panic!("Unsupported expr: {:?}", build_expr_debug_strings(e))
_ => panic!("Unsupported expr: {:?}", build_expr_debug_strings(e)),
}
}
fn writeline(e: &ExprAST, fop: &mut IroncamelFileInfo) {
Expand All @@ -90,10 +111,16 @@ fn writeline(e: &ExprAST, fop: &mut IroncamelFileInfo) {
}

enum ArithmeticCalcOp {
Add, Minus, Multiple
Add,
Minus,
Multiple,
}
enum ArithmeticCmpOp {
Gt, Lt, Geq, Leq, Eq
Gt,
Lt,
Geq,
Leq,
Eq,
}

pub fn call_builtin_function(func_name: &str, params: Vec<ExprAST>) -> ExprAST {
Expand All @@ -104,70 +131,65 @@ pub fn call_builtin_function(func_name: &str, params: Vec<ExprAST>) -> ExprAST {
"<" => arithmetic_cmp(ArithmeticCmpOp::Lt, &params),
">=" => arithmetic_cmp(ArithmeticCmpOp::Geq, &params),
"<=" => arithmetic_cmp(ArithmeticCmpOp::Leq, &params),
"+" => arithmetic_calc(ArithmeticCalcOp::Add, &params),
"-" => arithmetic_calc(ArithmeticCalcOp::Minus, &params),
"*" => arithmetic_calc(ArithmeticCalcOp::Multiple, &params),
"list" => {
ExprAST::List(Rc::new(IroncamelLinkedList::build_list(params.as_slice())))
}
"+" => arithmetic_calc(ArithmeticCalcOp::Add, &params),
"-" => arithmetic_calc(ArithmeticCalcOp::Minus, &params),
"*" => arithmetic_calc(ArithmeticCalcOp::Multiple, &params),
"list" => ExprAST::List(Rc::new(IroncamelLinkedList::build_list(params.as_slice()))),
"cons" => {
assert_eq!(params.len(), 2);
let tail = match &params[1] {
ExprAST::List(l) => l,
_ => panic!("Expect a list as the second param, got {:?}",&params[1])
_ => panic!("Expect a list as the second param, got {:?}", &params[1]),
};
let result = IroncamelLinkedList::cons(params[0].clone(),
tail);
let result = IroncamelLinkedList::cons(params[0].clone(), tail);
ExprAST::List(Rc::new(result))
},
}
"hd" => {
assert_eq!(params.len(), 1);
match &params[0] {
ExprAST::List(l) => l.hd().clone(),
_ => panic!("Expect a list, got {:?}",&params[0])
_ => panic!("Expect a list, got {:?}", &params[0]),
}
},
}
"tl" => {
assert_eq!(params.len(), 1);
match &params[0] {
ExprAST::List(l) => {
match l.tl() {
Some(t) => ExprAST::List(t),
None => build_empty_list_expr()
}
ExprAST::List(l) => match l.tl() {
Some(t) => ExprAST::List(t),
None => build_empty_list_expr(),
},
_ => panic!("Expect a list, got {:?}",&params[0])
_ => panic!("Expect a list, got {:?}", &params[0]),
}
},
}
"is_empty" => {
assert_eq!(params.len(), 1);
match &params[0] {
ExprAST::List(l) => {
let r = l.len == 0;
ExprAST::Bool(r)
},
_ => panic!("Expect a list, got {:?}",&params[0])
}
_ => panic!("Expect a list, got {:?}", &params[0]),
}
},
}
"atoi" => {
assert_eq!(params.len(), 1);
match &params[0] {
ExprAST::StringLiteral(s) => {
let x:i64 = s.parse().unwrap();
let x: i64 = s.parse().unwrap();
ExprAST::Int(x)
},
_ => panic!("Expect a String, got {:?}",&params[0])
}
_ => panic!("Expect a String, got {:?}", &params[0]),
}
},
}
"strtok" => {
assert_eq!(params.len(), 2);
let origin_str = match &params[0] {
ExprAST::StringLiteral(s) => s,
_ => panic!("Expect a String, got {:?}",&params[0])
_ => panic!("Expect a String, got {:?}", &params[0]),
};
let delims = match &params[1] {
ExprAST::StringLiteral(s) => s,
_ => panic!("Expect a String, got {:?}",&params[0])
_ => panic!("Expect a String, got {:?}", &params[0]),
};
debug!("got delims {}", delims);
let delim_list: Vec<char> = delims.chars().collect();
Expand All @@ -176,21 +198,18 @@ pub fn call_builtin_function(func_name: &str, params: Vec<ExprAST>) -> ExprAST {
debug!("split: {:?}", split_str);
let mut result: Vec<ExprAST> = Vec::new();
for s in split_str {
if s.is_empty() { continue }
if s.is_empty() {
continue;
}
let e = ExprAST::StringLiteral(s.to_owned());
result.push(e);
}
ExprAST::List(
Rc::new(IroncamelLinkedList::build_list(&result)
))

},
_ => panic!("Builtin function ({}) not found", func_name)
ExprAST::List(Rc::new(IroncamelLinkedList::build_list(&result)))
}
_ => panic!("Builtin function ({}) not found", func_name),
}
}



fn arithmetic_calc(op: ArithmeticCalcOp, p: &Vec<ExprAST>) -> ExprAST {
assert_eq!(p.len(), 2);
let a = unpack_num(&p[0]);
Expand All @@ -212,34 +231,33 @@ fn arithmetic_cmp(op: ArithmeticCmpOp, p: &Vec<ExprAST>) -> ExprAST {
ArithmeticCmpOp::Gt => a > b,
ArithmeticCmpOp::Lt => a < b,
ArithmeticCmpOp::Geq => a >= b,
ArithmeticCmpOp::Leq => a <= b
ArithmeticCmpOp::Leq => a <= b,
};
ExprAST::Bool(result)
}

fn unpack_num(e: &ExprAST) -> i64 {
match e {
ExprAST::Int(x) => *x,
_ => panic!("Expected int, got {:?}", build_expr_debug_strings(e))
_ => panic!("Expected int, got {:?}", build_expr_debug_strings(e)),
}
}


pub struct IroncamelLinkedList {
value: Box<ExprAST>,
pub(crate) len: usize, // Allows us to calculate list size with O(1) cost
next: Option<std::rc::Rc<IroncamelLinkedList>>
next: Option<std::rc::Rc<IroncamelLinkedList>>,
}

pub fn build_empty_list_expr() -> ExprAST {
ExprAST::List(Rc::new(IroncamelLinkedList::build_empty_list()))
}
impl IroncamelLinkedList {
pub(crate) fn build_empty_list() -> IroncamelLinkedList{
pub(crate) fn build_empty_list() -> IroncamelLinkedList {
IroncamelLinkedList {
value: Box::new(ExprAST::Error),
len: 0,
next: None
next: None,
}
}
pub fn build_list(exprs: &[ExprAST]) -> IroncamelLinkedList {
Expand All @@ -250,7 +268,7 @@ impl IroncamelLinkedList {
IroncamelLinkedList::build(exprs[0].clone())
} else {
let tail_exprs = &exprs[1..];
assert_eq!(exprs.len(), tail_exprs.len()+1);
assert_eq!(exprs.len(), tail_exprs.len() + 1);
let tail_link = IroncamelLinkedList::build_list(tail_exprs);
assert_eq!(tail_link.len, tail_exprs.len());
IroncamelLinkedList::cons(exprs[0].clone(), &Rc::new(tail_link))
Expand All @@ -261,7 +279,7 @@ impl IroncamelLinkedList {
IroncamelLinkedList {
value: Box::new(expr),
len: 1,
next: None
next: None,
}
}
pub fn cons(expr: ExprAST, tail: &Rc<IroncamelLinkedList>) -> IroncamelLinkedList {
Expand Down Expand Up @@ -290,9 +308,11 @@ impl IroncamelLinkedList {
vec![self.value.clone()]
} else {
match &self.next {
None => { panic!("Expected to have a tail") }
None => {
panic!("Expected to have a tail")
}
Some(next) => {
assert_eq!(next.len+1, self.len);
assert_eq!(next.len + 1, self.len);
let mut result = next.as_vector();
result.push(self.value.clone());
result
Expand All @@ -307,7 +327,7 @@ impl IroncamelLinkedList {
for i in 0..self.len {
let v = match *exprs[i] {
ExprAST::Int(x) => x,
_ => panic!("Expect an integer")
_ => panic!("Expect an integer"),
};
result.push(v);
}
Expand All @@ -321,23 +341,23 @@ impl Clone for IroncamelLinkedList {
// https://stackoverflow.com/a/61950053/1166518
let next = match &self.next {
Some(s) => Some(std::rc::Rc::clone(s)),
None => None
None => None,
};
IroncamelLinkedList {
value: self.value.clone(),
len: self.len,
next
next,
}
}
}


#[cfg(test)]
mod tests {
use crate::builtin::IroncamelLinkedList;
use crate::expr::ExprAST;

fn gei(x:i64) -> ExprAST { //generate expr int
fn gei(x: i64) -> ExprAST {
//generate expr int
ExprAST::Int(x)
}
#[test]
Expand All @@ -364,15 +384,13 @@ mod tests {
let v = l2.hd();
match v {
ExprAST::Int(x) => assert_eq!(*x, 42),
_ => assert!(false)
_ => assert!(false),
};

let l3 = l2.tl();
match l3 {
Some(l3) => assert_eq!(l3.as_vector_i64(), vec![5]),
None => assert!(false)
None => assert!(false),
};

}
}

Loading
Loading