#[macro_use]
extern crate pest;
#[macro_use]
extern crate pest_derive;
extern crate rand;
use std::cell::RefCell;
use std::rc::Rc;
use std::fs::File;
use std::io::prelude::*;
use std::fmt;
use std::fmt::Write;
use std::collections::HashMap;
use pest::Parser;
use pest::error::{Error, ErrorVariant};
use pest::iterators::Pair;
use rand::prelude::*;
#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct PseudocodeParser;
pub struct Program {
instructions: Vec<Instruction>
}
impl Program {
fn new(insts: Vec<Instruction>) -> Self {
Program{
instructions: insts,
}
}
}
#[derive(Clone, Debug)]
pub enum Op {
NEQ,
EQ,
GT,
LT,
GTE,
LTE,
MULT,
DIV,
PLUS,
MINUS,
MOD
}
#[derive(Clone, Debug)]
pub enum Expr {
ProcedureCall(String, Vec<Expr>),
Number(f64),
String(String),
Boolean(bool),
Identifier(String),
BinaryExpr(Box<Expr>, Op, Box<Expr>),
Negate(Box<Expr>),
Not(Box<Expr>),
List(Vec<Expr>),
Index(String, Box<Expr>)
}
#[derive(Clone, Debug)]
pub enum Instruction {
ProcedureDef(String, Vec<String>, Vec<Instruction>),
RepeatUntilBlock(Expr, Vec<Instruction>),
RepeatTimes(Expr, Vec<Instruction>),
ForEach(String, String, Vec<Instruction>),
IfSelection(Expr, Vec<Instruction>),
IfElseSelection(Expr, Vec<Instruction>, Vec<Instruction>),
ReturnStmt(Expr),
DisplayStmt(Expr),
ProcedureCall(String, Vec<Expr>),
IdentAssignment(String, Expr),
IndexAssignment(String, Expr, Expr)
}
#[derive(Clone, Debug)]
pub enum Value {
Number(f64),
String(String),
Boolean(bool),
List(Rc<RefCell<Vec<Rc<RefCell<Value>>>>>)
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Number(val) => write!(f, "{}", val),
Value::String(val) => write!(f, "{}", val),
Value::Boolean(val) => write!(f, "{}", val),
Value::List(list) => {
write!(f, "[")?;
let numvals = list.borrow().len();
for (idx, val) in list.borrow().iter().enumerate() {
if idx < numvals-1 {
write!(f, "{}, ", *val.borrow())?;
} else {
write!(f, "{}", *val.borrow())?;
}
}
write!(f, "]")
}
}
}
}
pub struct Procedure {
arglist: Vec<String>,
body: Vec<Instruction>,
native: Option<Box<dyn Fn(Rc<RefCell<Scope>>) -> Result<Vec<Instruction>, String>>>
}
impl Procedure {
fn new(arglist: Vec<String>, body: Vec<Instruction>, native: Option<Box<dyn Fn(Rc<RefCell<Scope>>) -> Result<Vec<Instruction>, String>>>) -> Self {
Procedure { arglist: arglist, body: body, native: native}
}
}
fn call_proc(scope: Rc<RefCell<Scope>>, name: String, args: Vec<Expr>, output: Rc<RefCell<String>>) -> Result<Option<Rc<RefCell<Value>>>, String> {
if let Some(_proc) = scope.clone().borrow().get_proc(&name) {
let proc = _proc.clone();
let func_scope = Rc::new(RefCell::new(Scope::new(scope.clone())));
for (idx, varname) in proc.borrow().arglist.iter().enumerate() {
let expr = &args[idx];
let evaluated = expr.eval(scope.clone(), output.clone())?;
func_scope.borrow_mut().variables.insert(varname.to_string(), evaluated);
}
if let Some(native_impl) = &proc.borrow().native {
let instructions = native_impl(func_scope.clone())?;
return run_instructions(instructions, func_scope, output.clone());
}
return run_instructions(proc.borrow().body.clone(), func_scope, output.clone());
} else {
return Err(format!("No procedure named {}", name));
}
}
pub struct Scope<'a> {
variables: HashMap<String, Rc<RefCell<Value>>>,
procedures: HashMap<String, Rc<RefCell<Procedure>>>,
parent: Option<Rc<RefCell<Scope<'a>>>>,
}
impl<'a> Scope<'a> {
fn new(parent: Rc<RefCell<Scope<'a>>>) -> Self {
Scope {
variables: HashMap::new(),
procedures: HashMap::new(),
parent: Some(parent),
}
}
fn new_base_scope() -> Self {
let mut s = Scope {
variables: HashMap::new(),
procedures: HashMap::new(),
parent: None,
};
let random_func = Procedure::new(vec!["a".to_string(), "b".to_string()], vec![], Some(Box::new(|scope| {
let mut rng = rand::thread_rng();
let a = scope.borrow().resolve("a".to_string()).ok_or("a is undefined")?;
let b = scope.borrow().resolve("b".to_string()).ok_or("a is undefined")?;
let val = match (a.borrow().clone(), b.borrow().clone()) {
(Value::Number(a), Value::Number(b)) => {
rng.gen_range(a as i64, (b as i64)+1)
},
_ => {
return Err("a and b most both be integers".to_string())
}
};
Ok(vec![
Instruction::ReturnStmt(Expr::Number(val as f64)),
])
})));
s.procedures.insert("RANDOM".to_string(), Rc::new(RefCell::new(random_func)));
let concat_func = Procedure::new(vec!["a".to_string(), "b".to_string()], vec![], Some(Box::new(|scope| {
let a = scope.borrow().resolve("a".to_string()).ok_or("a is undefined")?;
let b = scope.borrow().resolve("b".to_string()).ok_or("a is undefined")?;
let val = match (a.borrow().clone(), b.borrow().clone()) {
(Value::String(a), Value::String(b)) => {
format!("{}{}", a, b)
},
_ => {
return Err("a and b most both be strings".to_string())
}
};
Ok(vec![
Instruction::ReturnStmt(Expr::String(val)),
])
})));
s.procedures.insert("CONCAT".to_string(), Rc::new(RefCell::new(concat_func)));
let length_func = Procedure::new(vec!["l".to_string()], vec![], Some(Box::new(|scope| {
let list_val = scope.borrow().resolve("l".to_string()).ok_or("list is undefined")?;
let val = match list_val.borrow().clone() {
Value::List(list) => list.borrow().len() as f64,
_ => return Err("Argument to LENGTH must be a list".to_string())
};
Ok(vec![Instruction::ReturnStmt(Expr::Number(val))])
})));
s.procedures.insert("LENGTH".to_string(), Rc::new(RefCell::new(length_func)));
let insert_func = Procedure::new(vec!["list".to_string(), "i".to_string(), "value".to_string()], vec![], Some(Box::new(|scope| {
let list_val = scope.borrow().resolve("list".to_string()).ok_or("list is undefined")?;
let list = match list_val.borrow().clone() {
Value::List(list) => list,
_ => return Err("First argument to INSERT must be a list".to_string())
};
let idx_val = scope.borrow().resolve("i".to_string()).ok_or("i is undefined")?;
let idx = match *idx_val.borrow() {
Value::Number(idx) => if idx.fract() != 0.0 {
return Err("Second argument to INSERT must be an integer".to_string())
} else if idx < 1.0 || idx > list.borrow().len() as f64 {
return Err("Second argument to INSERT must be between 1 and the length of the list".to_string())
} else {
idx as usize
},
_ => return Err("First argument to INDEX must be a list".to_string())
};
let value = scope.borrow().resolve("value".to_string()).ok_or("value is undefined")?;
list.borrow_mut().insert(idx - 1, value);
Ok(vec![])
})));
s.procedures.insert("INSERT".to_string(), Rc::new(RefCell::new(insert_func)));
let remove_func = Procedure::new(vec!["list".to_string(), "i".to_string()], vec![], Some(Box::new(|scope| {
let list_val = scope.borrow().resolve("list".to_string()).ok_or("list is undefined")?;
let list = match list_val.borrow().clone() {
Value::List(list) => list,
_ => return Err("First argument to REMOVE must be a list".to_string())
};
let idx_val = scope.borrow().resolve("i".to_string()).ok_or("i is undefined")?;
let idx = match *idx_val.borrow() {
Value::Number(idx) => if idx.fract() != 0.0 {
return Err("Second argument to REMOVE must be an integer".to_string())
} else {
idx as usize
},
_ => return Err("First argument to REMOVE must be a list".to_string())
};
list.borrow_mut().remove(idx);
Ok(vec![])
})));
s.procedures.insert("REMOVE".to_string(), Rc::new(RefCell::new(remove_func)));
let append_func = Procedure::new(vec!["list".to_string(), "value".to_string()], vec![], Some(Box::new(|scope| {
let list_val = scope.borrow().resolve("list".to_string()).ok_or("list is undefined")?;
let list = match list_val.borrow().clone() {
Value::List(list) => list,
_ => return Err("First argument to APPEND must be a list".to_string())
};
let value = scope.borrow().resolve("value".to_string()).ok_or("value is undefined")?;
list.borrow_mut().push(value);
Ok(vec![])
})));
s.procedures.insert("APPEND".to_string(), Rc::new(RefCell::new(append_func)));
return s
}
fn define_proc(&mut self, name: String, arglist: Vec<String>, body: Vec<Instruction>) {
self.procedures.insert(name,
Rc::new(RefCell::new(Procedure::new(arglist, body, None))));
}
fn defined_in_parent(&self, ident: &String) -> bool {
if let Some(parent_scope) = &self.parent {
if parent_scope.borrow().variables.contains_key(ident) {
return true
} else {
parent_scope.borrow().defined_in_parent(ident)
}
} else {
false
}
}
fn assign(&mut self, ident: String, value: Rc<RefCell<Value>>) {
if self.variables.contains_key(&ident) {
self.variables.insert(ident, value);
} else if self.defined_in_parent(&ident) {
if let Some(parent_scope) = &self.parent {
parent_scope.borrow_mut().assign(ident, value);
}
} else {
self.variables.insert(ident, value);
}
}
fn resolve(&self, ident: String) -> Option<Rc<RefCell<Value>>> {
match self.variables.get(&ident) {
Some(val) => Some(val.clone()),
None => match &self.parent {
Some(parent_scope) => parent_scope.borrow().resolve(ident),
None => None
}
}
}
fn get_proc(&self, ident: &String) -> Option<Rc<RefCell<Procedure>>> {
match self.procedures.get(ident) {
Some(val) => Some(val.clone()),
None => match &self.parent {
Some(parent_scope) => parent_scope.borrow().get_proc(ident),
None => None
}
}
}
fn dump(&self) {
println!("### DUMPING SCOPE ###");
for (var, val) in self.variables.iter() {
println!("{} => {:?}", var, val);
}
}
}
impl Expr {
fn eval(&self, scope: Rc<RefCell<Scope>>, output: Rc<RefCell<String>>) -> Result<Rc<RefCell<Value>>, String> {
match self {
Expr::Number(val) => Ok(Rc::new(RefCell::new(Value::Number(*val)))),
Expr::String(val) => Ok(Rc::new(RefCell::new(Value::String(val.clone())))),
Expr::Boolean(val) => Ok(Rc::new(RefCell::new(Value::Boolean(val.clone())))),
Expr::Identifier(ident) => match scope.borrow().resolve(ident.to_string()) {
Some(val) => Ok(val),
None => Err(format!("Variable {} is not defined", ident))
},
Expr::Negate(expr) => {
let val = expr.eval(scope.clone(), output.clone())?.borrow().clone();
match val {
Value::Number(n) => Ok(Rc::new(RefCell::new(Value::Number(-n)))),
_ => Err(format!("Value {} must be a number", val))
}
},
Expr::Not(expr) => {
let val = expr.eval(scope.clone(), output.clone())?.borrow().clone();
match val {
Value::Boolean(n) => Ok(Rc::new(RefCell::new(Value::Boolean(!n)))),
_ => Err(format!("Value {} must be a boolean value but was a {:?}", val, val)),
}
},
Expr::BinaryExpr(lhs, op, rhs) => {
let lhsval = lhs.eval(scope.clone(), output.clone())?;
let rhsval = rhs.eval(scope.clone(), output.clone())?;
match op {
Op::PLUS => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Number(f1 + f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must both be numbers", lhsval, rhsval))
}
}
},
Op::MINUS => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Number(f1 - f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must both be numbers", lhsval, rhsval))
}
}
},
Op::MULT => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Number(f1 * f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must both be numbers", lhsval, rhsval))
}
}
},
Op::DIV => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Number(f1 / f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must both be numbers", lhsval, rhsval))
}
}
},
Op::MOD => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Number(f1 % f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must both be numbers", lhsval, rhsval))
}
}
},
Op::EQ => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 == f2))))
},
(Value::String(f1), Value::String(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 == f2))))
},
(Value::Boolean(f1), Value::Boolean(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 == f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must be same type", lhsval, rhsval))
}
}
},
Op::NEQ => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 != f2))))
},
(Value::String(f1), Value::String(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 != f2))))
},
(Value::Boolean(f1), Value::Boolean(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 != f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must be same type", lhsval, rhsval))
}
}
},
Op::GT => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 > f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must be same type", lhsval, rhsval))
}
}
},
Op::LT => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 < f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must be same type", lhsval, rhsval))
}
}
},
Op::GTE => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 >= f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must be same type", lhsval, rhsval))
}
}
},
Op::LTE => {
match (lhsval.borrow().clone(), rhsval.borrow().clone()) {
(Value::Number(f1), Value::Number(f2)) => {
Ok(Rc::new(RefCell::new(Value::Boolean(f1 <= f2))))
},
(_, _) => {
Err(format!("{:?} and {:?} must be same type", lhsval, rhsval))
}
}
},
}
},
Expr::ProcedureCall(procname, arglist) => {
let value = call_proc(scope.clone(), procname.to_string(), arglist.clone(), output.clone())?;
if let Some(value) = value {
Ok(value)
} else {
Ok(Rc::new(RefCell::new(Value::Number(-1.0))))
}
},
Expr::List(list) => {
let mut list_val: Vec<Rc<RefCell<Value>>> = Vec::new();
for vexpr in list.iter() {
let val = vexpr.eval(scope.clone(), output.clone())?;
list_val.push(val)
}
let l = Rc::new(RefCell::new(list_val));
Ok(Rc::new(RefCell::new(Value::List(l))))
},
Expr::Index(list_ident, index_expr) => {
let list_val = scope.borrow().resolve(list_ident.to_string())
.expect(&format!("List variable {} is not defined", list_ident)).borrow().clone();
if let Value::List(list) = list_val {
let index_val = index_expr.eval(scope.clone(), output.clone())?;
if let Value::Number(index) = *index_val.borrow() {
if index.fract() != 0.0 {
return Err(format!("Index {} must be an integer", *index_val.borrow()))
}
match list.borrow().get(index as usize-1) {
Some(val) => return Ok(val.clone()),
None => return Err(format!("List index {} is out of range {:?}", index, list))
};
} else {
return Err(format!("Index {} must be an integer", *index_val.borrow()));
};
} else {
return Err(format!("Variable {} must be a list", list_ident))
}
}
}
}
}
pub fn parse_op(pair: Pair<Rule>) -> Op {
let op = pair.into_inner().next().unwrap();
match op.as_rule() {
Rule::neq => Op::NEQ,
Rule::eq => Op::EQ,
Rule::gt => Op::GT,
Rule::lt => Op::LT,
Rule::gte => Op::GTE,
Rule::lte => Op::LTE,
Rule::mult => Op::MULT,
Rule::div => Op::DIV,
Rule::plus => Op::PLUS,
Rule::minus => Op::MINUS,
Rule::modop => Op::MOD,
_ => { panic!("{:#?} is unknown", op); }
}
}
fn parse_value_inner(val: Pair<Rule>) -> Expr {
match val.as_rule() {
Rule::procedure_call => {
let mut block = val.into_inner();
let ident = block.next().unwrap().as_str().to_string();
let arg_list: Vec<Expr> = block.next().unwrap().into_inner().map(parse_expr).collect();
Expr::ProcedureCall(ident, arg_list)
},
Rule::number => {
let val: f64 = val.as_str().parse().unwrap();
Expr::Number(val)
},
Rule::string => {
Expr::String(val.into_inner().next().unwrap().as_str().to_string())
},
Rule::boolean => {
Expr::Boolean(val.as_str() == "true")
},
Rule::identifier => {
Expr::Identifier(val.as_str().trim().to_string())
},
Rule::expression => {
parse_expr(val)
},
Rule::list => {
let x: Vec<Expr> = val.into_inner().map(|v| {
parse_expr(v)
}).collect();
Expr::List(x)
},
Rule::index => {
let mut block = val.into_inner();
let ident = block.next().unwrap().as_str().to_string();
let index = parse_expr(block.next().unwrap());
Expr::Index(ident, Box::new(index))
},
_ => {
panic!("UNEXPECTED VALUE parse_value_inner {:?}", val);
}
}
}
pub fn parse_value(pair: Pair<Rule>) -> Expr {
let mut block = pair.into_inner();
let val = block.next().unwrap();
match val.as_rule() {
Rule::prefix_op => {
let inner_val = parse_value_inner(block.next().unwrap());
match val.into_inner().next().unwrap().as_rule() {
Rule::minus => Expr::Negate(Box::new(inner_val)),
Rule::not => Expr::Not(Box::new(inner_val)),
_ => unreachable!()
}
}
_ => parse_value_inner(val)
}
}
pub fn parse_expr(pair: Pair<Rule>) -> Expr {
match pair.as_rule() {
Rule::value => parse_value(pair),
Rule::expression => {
let mut parts = pair.into_inner();
let mut lhs = parse_expr(parts.next().unwrap());
while let Some(_) = parts.peek() {
let op = parse_op(parts.next().unwrap());
let rhs = parse_expr(parts.next().unwrap());
lhs = Expr::BinaryExpr(Box::new(lhs), op, Box::new(rhs));
}
return lhs;
},
_ => {
panic!("other {:?}", pair);
}
}
}
pub fn parse_block(block: Pair<Rule>) -> Vec<Instruction> {
let mut instructions: Vec<Instruction> = Vec::new();
for inst in block.into_inner() {
let bi = inst.into_inner().next().unwrap();
match bi.as_rule() {
Rule::selection_block => {
if let Some(sel_block) = parse_selection(bi) {
instructions.push(sel_block);
}
},
Rule::assignment => {
let mut block = bi.into_inner();
let symbol = block.next().unwrap();
block.next();
match symbol.as_rule() {
Rule::identifier => {
let ident = symbol.as_str().trim().to_string();
let expr = block.next().unwrap();
instructions.push(Instruction::IdentAssignment(ident, parse_expr(expr)));
},
_ => {
panic!("UNEXPECTED parse_block {:?}", symbol);
}
}
},
Rule::return_statement => {
let expr = parse_expr(bi.into_inner().next().unwrap());
instructions.push(Instruction::ReturnStmt(expr));
}
Rule::display => {
let expr = parse_expr(bi.into_inner().next().unwrap());
instructions.push(Instruction::DisplayStmt(expr));
}
_ => {
panic!("other {:?}", bi);
}
}
}
return instructions;
}
pub fn parse_selection(sel: Pair<Rule>) -> Option<Instruction> {
let inst = sel.into_inner().next().unwrap();
match inst.as_rule() {
Rule::if_expr => {
let mut block = inst.into_inner();
let cond_expr = parse_expr(block.next().unwrap());
let cond_inst = parse_block(block.next().unwrap());
Some(Instruction::IfSelection(cond_expr, cond_inst))
},
Rule::if_else_expr => {
let mut block = inst.into_inner();
let cond_expr = parse_expr(block.next().unwrap());
let cond_inst = parse_block(block.next().unwrap());
let else_inst = parse_block(block.next().unwrap());
Some(Instruction::IfElseSelection(cond_expr, cond_inst, else_inst))
},
_ => { None }
}
}
pub fn parse_prog(prog: &str) -> Result<Program, Error<Rule>> {
if let Err(a) = PseudocodeParser::parse(Rule::program, prog) {
println!("syntax error at {:?}", a.location);
println!("syntax error at line col: {:?}", a.line_col);
match a.variant {
ErrorVariant::ParsingError{positives, negatives: _} => {
println!("positive? {:?}", positives.get(0).unwrap());
},
ErrorVariant::CustomError{message: _} => {},
}
};
let parsed = PseudocodeParser::parse(Rule::program, prog)?;
let mut instructions: Vec<Instruction> = Vec::new();
for inst in parsed {
match inst.as_rule() {
Rule::selection_block => {
if let Some(sel_block) = parse_selection(inst) {
instructions.push(sel_block);
}
},
Rule::iteration_block => {
let block = inst.into_inner().next().unwrap();
match block.as_rule() {
Rule::repeat_until => {
let mut ib = block.into_inner();
let expr = parse_expr(ib.next().unwrap());
let inst = parse_block(ib.next().unwrap());
instructions.push(Instruction::RepeatUntilBlock(expr, inst));
},
Rule::repeat_times => {
let mut ib = block.into_inner();
let times = parse_expr(ib.next().unwrap());
let inst = parse_block(ib.next().unwrap());
instructions.push(Instruction::RepeatTimes(times, inst));
},
Rule::for_each => {
let mut ib = block.into_inner();
let ident = ib.next().unwrap().as_str().trim().to_string();
let loopvar = ib.next().unwrap().as_str().trim().to_string();
let inst = parse_block(ib.next().unwrap());
instructions.push(Instruction::ForEach(loopvar, ident, inst));
},
_ => {
panic!("other {:?}", block);
}
}
},
Rule::procedure_def => {
let mut block = inst.into_inner();
let proc_name = block.next().unwrap().as_str().to_string();
let arg_list: Vec<String> = block.next().unwrap().into_inner().map(|pair|
pair.as_str().to_string()
).collect();
let proc_list = parse_block(block.next().unwrap());
instructions.push(Instruction::ProcedureDef(proc_name, arg_list, proc_list));
},
Rule::assignment => {
let mut block = inst.into_inner();
let symbol = block.next().unwrap();
block.next();
match symbol.as_rule() {
Rule::identifier => {
let ident = symbol.as_str().trim().to_string();
let expr = block.next().unwrap();
instructions.push(Instruction::IdentAssignment(ident, parse_expr(expr)));
},
Rule::index => {
let mut index_sym = symbol.into_inner();
let list_ident = index_sym.next().unwrap().as_str().trim().to_string();
let list_index = index_sym.next().unwrap();
let list_value = block.next().unwrap();
instructions.push(Instruction::IndexAssignment(list_ident, parse_expr(list_index), parse_expr(list_value)));
},
_ => {
panic!("UNEXPECTED parse_block {:?}", symbol);
}
}
},
Rule::display => {
let inner = inst.into_inner().next().unwrap();
let expr = parse_expr(inner);
instructions.push(Instruction::DisplayStmt(expr));
}
Rule::expression => {
let inner = inst.into_inner().next().unwrap().into_inner().next().unwrap();
match inner.as_rule() {
Rule::procedure_call => {
let mut call = inner.into_inner();
let ident = call.next().unwrap().as_str().trim().to_string();
let arg_list: Vec<Expr> = call.next().unwrap().into_inner().map(parse_expr).collect();
instructions.push(Instruction::ProcedureCall(ident, arg_list));
},
_ => {}
}
},
_ => {}
}
}
Ok(Program::new(instructions))
}
fn run_instructions(instructions: Vec<Instruction>, scope: Rc<RefCell<Scope>>, output: Rc<RefCell<String>>) -> Result<Option<Rc<RefCell<Value>>>, String> {
let mut return_value: Option<Rc<RefCell<Value>>> = None;
for instruction in instructions.iter() {
match instruction {
Instruction::ProcedureDef(name, arglist, block) => {
scope.borrow_mut().define_proc(name.to_string(), arglist.to_vec(), block.to_vec());
},
Instruction::RepeatUntilBlock(expr, block) => {
loop {
if let Value::Boolean(doloop) = *expr.eval(scope.clone(), output.clone())?.borrow() {
if !doloop {
run_instructions(block.to_vec(), scope.clone(), output.clone())?;
} else {
break
}
}
}
},
Instruction::RepeatTimes(expr, block) => {
if let Value::Number(ntimes) = *expr.eval(scope.clone(), output.clone())?.borrow() {
for _ in 0..(ntimes as u64) {
run_instructions(block.to_vec(), scope.clone(), output.clone())?;
}
}
},
Instruction::ForEach(list_ident, loopvar, block) => {
if let Value::List(list) = scope.borrow().resolve(list_ident.to_string())
.expect(&format!("List variable {} is not defined", list_ident)).borrow().clone() {
for item in list.borrow().iter() {
let loop_scope = Rc::new(RefCell::new(Scope::new(scope.clone())));
loop_scope.borrow_mut().variables.insert(loopvar.to_string(), item.clone());
run_instructions(block.to_vec(), loop_scope, output.clone())?;
}
}
},
Instruction::IfSelection(cond, block) => {
if let Value::Boolean(expr) = *cond.eval(scope.clone(), output.clone())?.borrow() {
if expr {
return_value = run_instructions(block.to_vec(), scope.clone(), output.clone())?;
}
}
},
Instruction::IfElseSelection(cond, block, elseblock) => {
if let Value::Boolean(expr) = *cond.eval(scope.clone(), output.clone())?.borrow() {
if expr {
return_value = run_instructions(block.to_vec(), scope.clone(), output.clone())?;
} else {
return_value = run_instructions(elseblock.to_vec(), scope.clone(), output.clone())?;
}
}
},
Instruction::ProcedureCall(procname, arglist) => {
return_value = call_proc(scope.clone(), procname.to_string(), arglist.clone(), output.clone())?;
}
Instruction::DisplayStmt(expr) => {
let value = expr.eval(scope.clone(), output.clone())?;
match write!(&mut output.borrow_mut(), "{} ", *value.borrow()) {
Err(e) => panic!("Could not print to output: {}", e),
_ => {}
};
},
Instruction::IdentAssignment(ident, expr) => {
let value = expr.eval(scope.clone(), output.clone())?;
scope.borrow_mut().assign(ident.to_string(), value);
},
Instruction::IndexAssignment(list_ident, list_index, list_value) => {
let list_val = scope.borrow().resolve(list_ident.to_string())
.expect(&format!("List variable {} is not defined", list_ident));
if let Value::List(list) = list_val.borrow().clone() {
let index_val = list_index.eval(scope.clone(), output.clone())?;
if let Value::Number(index) = *index_val.borrow() {
if index.fract() != 0.0 {
return Err(format!("Index {} must be an integer", index))
}
if (index) < 1.0 || (index as usize) > list.borrow().len() {
return Err(format!("Index {} is out of range", index))
}
let value = list_value.eval(scope.clone(), output.clone())?;
list.borrow_mut()[index as usize - 1] = value;
let l = Rc::new(RefCell::new(Value::List(list)));
scope.borrow_mut().assign(list_ident.to_string(), l);
};
};
},
Instruction::ReturnStmt(expr) => {
let value = expr.eval(scope.clone(), output.clone())?;
return_value = Some(value);
}
}
}
Ok(return_value)
}
pub fn eval(prog: &str) -> Result<String, String> {
let output = Rc::new(RefCell::new(String::new()));
match parse_prog(prog) {
Ok(program) => {
let scope = Rc::new(RefCell::new(Scope::new_base_scope()));
run_instructions(program.instructions, scope.clone(), output.clone())?;
Ok(output.borrow().to_string())
},
Err(err) => Err(format!("Parse Error: {}", err.to_string()))
}
}
pub fn eval_file(filename: &str) -> Result<String, String> {
let mut file = File::open(filename).expect("Problem opening file");
let mut prog = String::new();
file.read_to_string(&mut prog).expect("Problem reading file");
eval(&prog)
}
#[cfg(test)]
mod tests {
use super::*;
use pest::parses_to;
macro_rules! map(
{ $($key:expr => $value:expr),+ } => {
{
let mut m = ::std::collections::HashMap::new();
$(
m.insert($key, $value);
)+
m
}
};
);
#[test]
fn test_parse() {
let success = PseudocodeParser::parse(Rule::program, "abc ← 3.0").unwrap();
println!("{:?}", success);
}
#[test]
fn test_parse_binary_expr() {
let success = PseudocodeParser::parse(Rule::expression, "(3 * 2) < (4 * 10.3)").unwrap();
println!("{:?}", success);
}
#[test]
fn test_parse_assignment_expr() {
let success = PseudocodeParser::parse(Rule::expression, "a ← 1 + 2").unwrap();
println!("{:#?}", success);
}
#[test]
fn test_file_output() -> Result<(), String> {
let file_output = map!(
"test_programs/assignment.ap" => "a is 3 b is 10.5 string_val is hello",
"test_programs/expr.ap" => "1 2 3 4 5 6 7 8 9 10",
"test_programs/loops.ap" => "1 2 3 x is even 2 x is odd 3 x is even 4 x is odd 5",
"test_programs/procedure.ap" => "inside did func work? true",
"test_programs/negate.ap" => "-1 -3 -6 -6 false true false true",
"test_programs/ops.ap" => "true false true",
"test_programs/list.ap" => "[] [1, 2, 3] [abc, def] [[1, 2, 3], [4, 5, 6]] [2, 7, 17.5]",
"test_programs/list2.ap" => "[1, 2, 3] [a, 1, 2, 3] [a, 1, b, 2, 3]",
"test_programs/recursion.ap" => "1 720"
);
for (file, expected_output) in file_output.iter() {
let output = eval_file(file).unwrap().trim().to_string();
assert!(output.eq(expected_output))
}
Ok(())
}
#[test]
fn test_parses_to() {
parses_to! {
parser: PseudocodeParser,
input: "my_variable",
rule: Rule::identifier,
tokens: [identifier(0, 11)]
}
parses_to! {
parser: PseudocodeParser,
input: "\"hello world\"",
rule: Rule::string,
tokens: [string(0, 13, [inner(1, 12)])]
}
}
}