Skip to content
Closed
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
67 changes: 67 additions & 0 deletions src/interpreter/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ pub fn eval(exp: Expression, env: &Environment<EnvValue>) -> Result<EnvValue, Er
Expression::IsError(e) => eval_iserror_expression(*e, env),
Expression::IsNothing(e) => eval_isnothing_expression(*e, env),
Expression::FuncCall(name, args) => call(name, args, env),
Expression::ReadFile(file_path_exp) => read_file(*file_path_exp, env),
Expression::ReadString => read_string(env),
Expression::ReadInt => read_int(env),
Expression::ReadFloat => read_float(env),

_ if is_constant(exp.clone()) => Ok(EnvValue::Exp(exp)),
_ => Err((String::from("Not implemented yet."), None)),
}
Expand Down Expand Up @@ -200,6 +205,33 @@ fn execute(stmt: Statement, env: &Environment<EnvValue>) -> Result<ControlFlow,
let exp_value = eval(*exp, &new_env)?;
Ok(ControlFlow::Return(exp_value))
}

Statement::WriteToFile(file_path_exp, content_exp) => {
let file_path_value = eval(*file_path_exp, &new_env)?;
let content_value = eval(*content_exp, &new_env)?;

if let (EnvValue::Exp(Expression::CString(file_path)), EnvValue::Exp(Expression::CString(content))) = (file_path_value, content_value) {
std::fs::write(file_path, content).map_err(|e| (e.to_string(), None))?;
Ok(ControlFlow::Continue(new_env))
} else {
Err((String::from("write_to_file expects two string arguments"), None))
}
}

Statement::Print(exp) => {
let value = eval(*exp, &new_env)?;

match value {
EnvValue::Exp(Expression::CInt(i)) => println!("{}", i),
EnvValue::Exp(Expression::CReal(r)) => println!("{}", r),
EnvValue::Exp(Expression::CString(s)) => println!("{}", s),
EnvValue::Exp(Expression::CTrue) => println!("true"),
EnvValue::Exp(Expression::CFalse) => println!("false"),
_ => return Err((String::from("Cannot print this type of value"), None)),
}

Ok(ControlFlow::Continue(new_env))
}
_ => Err((String::from("not implemented yet"), None)),
};

Expand Down Expand Up @@ -324,6 +356,41 @@ fn propagate_error(
))));
}
}
fn read_file(file_path_exp: Expression, env: &Environment<EnvValue>) -> Result<EnvValue, ErrorMessage> {
let file_path_value = eval(file_path_exp, env)?;
if let EnvValue::Exp(Expression::CString(file_path)) = file_path_value {
let content = std::fs::read_to_string(file_path).map_err(|e| (e.to_string(), None))?;
Ok(EnvValue::Exp(Expression::CString(content)))
} else {
Err((String::from("read_file expects a string as the file path"), None))
}
}


// IO helper functions
fn read_string(_: &Environment<EnvValue>) -> Result<EnvValue, ErrorMessage> {
let mut input = String::new();
std::io::stdin().read_line(&mut input).map_err(|e| (e.to_string(), None))?;

let input = input.trim().to_string();
Ok(EnvValue::Exp(Expression::CString(input)))
}

fn read_int(_: &Environment<EnvValue>) -> Result<EnvValue, ErrorMessage> {
let mut input = String::new();
std::io::stdin().read_line(&mut input).map_err(|e| (e.to_string(), None))?;

let input = input.trim().parse::<i32>().map_err(|e| (e.to_string(), None))?;
Ok(EnvValue::Exp(Expression::CInt(input)))
}

fn read_float(_: &Environment<EnvValue>) -> Result<EnvValue, ErrorMessage> {
let mut input = String::new();
std::io::stdin().read_line(&mut input).map_err(|e| (e.to_string(), None))?;

let input = input.trim().parse::<f64>().map_err(|e| (e.to_string(), None))?;
Ok(EnvValue::Exp(Expression::CReal(input)))
}

fn execute_tests(
tests_set: Vec<(String, Option<String>)>,
Expand Down
8 changes: 8 additions & 0 deletions src/ir/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@ pub enum Expression {
IsError(Box<Expression>),
IsNothing(Box<Expression>),
Propagate(Box<Expression>),

/* input expressions */
ReadFile(Box<Expression>),
ReadString,
ReadInt,
ReadFloat,
}

#[derive(Debug, PartialEq, Clone)]
Expand All @@ -214,6 +220,8 @@ pub enum Statement {
AssertFails(String),
FuncDef(Function),
Return(Box<Expression>),
WriteToFile(Box<Expression>, Box<Expression>),
Print(Box<Expression>),
}

#[derive(Debug)]
Expand Down
44 changes: 44 additions & 0 deletions src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ fn statement(input: &str) -> IResult<&str, Statement> {
return_statement,
assignment,
declaration,
print_statement,
write_to_file_statement,
))(input)
}

Expand All @@ -124,6 +126,9 @@ fn expression(input: &str) -> IResult<&str, Expression> {
iserror_expression,
isnothing_expression,
string,
read_string,
read_int,
read_float,
map(identifier, Expression::Var),
))(input)
}
Expand Down Expand Up @@ -379,6 +384,8 @@ fn factor(input: &str) -> IResult<&str, Expression> {
))(input)
}



//indented block parser
fn indented_block(input: &str) -> IResult<&str, Vec<Statement>> {
let (input, _) = line_ending(input)?;
Expand Down Expand Up @@ -464,6 +471,8 @@ fn parse_type(type_name: &str) -> Type {
}
}



// function definition parsing
fn function_def(input: &str) -> IResult<&str, Statement> {
let (input, _) = tag("def")(input)?;
Expand Down Expand Up @@ -501,6 +510,41 @@ fn function_def(input: &str) -> IResult<&str, Statement> {
))
}

//Parse IO functions
fn read_string(input: &str) -> IResult<&str, Expression> {
map(tag("read_string"), |_| Expression::ReadString)(input)
}

fn read_int(input: &str) -> IResult<&str, Expression> {
map(tag("read_int"), |_| Expression::ReadInt)(input)
}

fn read_float(input: &str) -> IResult<&str, Expression> {
map(tag("read_float"), |_| Expression::ReadFloat)(input)
}

fn print_statement(input: &str) -> IResult<&str, Statement> {
let (input, _) = tag("print")(input)?;
let (input, _) = space1(input)?;
let (input, expr) = expression(input)?;
Ok((input, Statement::Print(Box::new(expr))))
}

fn write_to_file_statement(input: &str) -> IResult<&str, Statement> {
let (input, _) = tag("write_to_file")(input)?;
let (input, _) = space1(input)?;

let (input, file_path) = expression(input)?;
let (input, _) = space0(input)?;
let (input, _) = char(',')(input)?;
let (input, _) = space0(input)?;

let (input, content) = expression(input)?;

Ok((input, Statement::WriteToFile(Box::new(file_path), Box::new(content))))
}


//return statement parsing
fn return_statement(input: &str) -> IResult<&str, Statement> {
let (input, _) = tag("return")(input)?;
Expand Down
39 changes: 39 additions & 0 deletions src/tc/type_checker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ pub fn check_exp(exp: Expression, env: &Environment<Type>) -> Result<Type, Error
Expression::Unwrap(e) => check_unwrap_type(*e, env),
Expression::Propagate(e) => check_propagate_type(*e, env),
Expression::FuncCall(name, args) => check_func_call(name, args, env),


Expression::ReadFile(file_path_exp) => check_read_file_expression(*file_path_exp, env),

Expression::ReadString => Ok(Type::TString),
Expression::ReadInt => Ok(Type::TInteger),
Expression::ReadFloat => Ok(Type::TReal),
//_ => Err(String::from("not implemented yet")),
}
}
Expand Down Expand Up @@ -166,6 +173,25 @@ pub fn check_stmt(stmt: Statement, env: &Environment<Type>) -> Result<ControlFlo
Err(format!("[Syntax Error] return statement outside function."))
}
}
Statement::WriteToFile(file_path_exp, content_exp) => {
let file_path_type = check_exp(*file_path_exp, &new_env)?;
let content_type = check_exp(*content_exp, &new_env)?;

if file_path_type != Type::TString || content_type != Type::TString {
return Err(String::from("write_to_file expects two string arguments"));
}

Ok(ControlFlow::Continue(new_env))
}

Statement::Print(exp) => {
let exp_type = check_exp(*exp, &new_env)?;

match exp_type {
Type::TInteger | Type::TReal | Type::TString | Type::TBool => Ok(ControlFlow::Continue(new_env)),
_ => Err(String::from("Cannot print this type of value")),
}
}
_ => Err(String::from("not implemented yet.")),
}
}
Expand Down Expand Up @@ -364,6 +390,19 @@ fn check_isnothing_type(exp: Expression, env: &Environment<Type>) -> Result<Type
}
}

fn check_read_file_expression(
file_path_exp: Expression,
env: &Environment<Type>,
) -> Result<Type, ErrorMessage> {
let file_path_type = check_exp(file_path_exp, env)?;

if file_path_type != Type::TString {
return Err(String::from("read_file expects a string as the file path"));
}

Ok(Type::TString)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down