Add rustfmt.toml and format tree walk evaluator:wq
This commit is contained in:
parent
0c6d2be95a
commit
a3463f5519
7
rustfmt.toml
Normal file
7
rustfmt.toml
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
max_width = 110
|
||||
use_small_heuristics = "max"
|
||||
imports_indent = "block"
|
||||
imports_granularity = "crate"
|
||||
group_imports = "stdexternalcrate"
|
||||
|
@ -1,12 +1,14 @@
|
||||
use crate::reduced_ir::{ReducedIR, Expression, Lookup, Callable, FunctionDefinition, Statement, Literal, Alternative, Pattern};
|
||||
use crate::symbol_table::{DefId};
|
||||
use crate::util::ScopeStack;
|
||||
use crate::builtin::Builtin;
|
||||
use crate::typechecking::TypeId;
|
||||
use std::{convert::From, fmt::Write, rc::Rc};
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::rc::Rc;
|
||||
use std::convert::From;
|
||||
use crate::{
|
||||
builtin::Builtin,
|
||||
reduced_ir::{
|
||||
Alternative, Callable, Expression, FunctionDefinition, Literal, Lookup, Pattern, ReducedIR, Statement,
|
||||
},
|
||||
symbol_table::DefId,
|
||||
typechecking::TypeId,
|
||||
util::ScopeStack,
|
||||
};
|
||||
|
||||
mod test;
|
||||
|
||||
@ -21,7 +23,7 @@ pub struct State<'a> {
|
||||
//evaluator
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
enum Memory {
|
||||
Index(u32)
|
||||
Index(u32),
|
||||
}
|
||||
|
||||
// This is for function param lookups, and is a hack
|
||||
@ -39,22 +41,18 @@ impl From<&DefId> for Memory {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RuntimeError {
|
||||
msg: String
|
||||
msg: String,
|
||||
}
|
||||
|
||||
impl From<String> for RuntimeError {
|
||||
fn from(msg: String) -> Self {
|
||||
Self {
|
||||
msg
|
||||
}
|
||||
Self { msg }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for RuntimeError {
|
||||
fn from(msg: &str) -> Self {
|
||||
Self {
|
||||
msg: msg.to_string(),
|
||||
}
|
||||
Self { msg: msg.to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +63,7 @@ impl RuntimeError {
|
||||
}
|
||||
}
|
||||
|
||||
fn paren_wrapped(terms: impl Iterator<Item=String>) -> String {
|
||||
fn paren_wrapped(terms: impl Iterator<Item = String>) -> String {
|
||||
let mut buf = String::new();
|
||||
write!(buf, "(").unwrap();
|
||||
for term in terms.map(Some).intersperse(None) {
|
||||
@ -92,7 +90,6 @@ impl From<Primitive> for MemoryValue {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl MemoryValue {
|
||||
fn to_repl(&self) -> String {
|
||||
match self {
|
||||
@ -126,26 +123,23 @@ enum Primitive {
|
||||
Tuple(Vec<Primitive>),
|
||||
Literal(Literal),
|
||||
Callable(Callable),
|
||||
Object {
|
||||
type_id: TypeId,
|
||||
tag: u32,
|
||||
items: Vec<Primitive>
|
||||
},
|
||||
Object { type_id: TypeId, tag: u32, items: Vec<Primitive> },
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
fn to_repl(&self) -> String {
|
||||
match self {
|
||||
Primitive::Object { type_id, items, .. } if items.is_empty() => type_id.local_name().to_string(),
|
||||
Primitive::Object { type_id, items, .. } =>
|
||||
format!("{}{}", type_id.local_name(), paren_wrapped(items.iter().map(|item| item.to_repl()))),
|
||||
Primitive::Object { type_id, items, .. } => {
|
||||
format!("{}{}", type_id.local_name(), paren_wrapped(items.iter().map(|item| item.to_repl())))
|
||||
}
|
||||
Primitive::Literal(lit) => match lit {
|
||||
Literal::Nat(n) => format!("{}", n),
|
||||
Literal::Int(i) => format!("{}", i),
|
||||
Literal::Float(f) => format!("{}", f),
|
||||
Literal::Bool(b) => format!("{}", b),
|
||||
Literal::StringLit(s) => format!("\"{}\"", s),
|
||||
}
|
||||
},
|
||||
Primitive::Tuple(terms) => paren_wrapped(terms.iter().map(|x| x.to_repl())),
|
||||
Primitive::Callable(..) => "<some-callable>".to_string(),
|
||||
}
|
||||
@ -164,9 +158,7 @@ impl From<Literal> for Primitive {
|
||||
|
||||
impl<'a> State<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
environments: ScopeStack::new(Some("global".to_string()))
|
||||
}
|
||||
Self { environments: ScopeStack::new(Some("global".to_string())) }
|
||||
}
|
||||
|
||||
pub fn evaluate(&mut self, reduced: ReducedIR, repl: bool) -> Vec<Result<String, String>> {
|
||||
@ -179,9 +171,7 @@ impl<'a> State<'a> {
|
||||
|
||||
for statement in reduced.entrypoint.into_iter() {
|
||||
match self.statement(statement) {
|
||||
Ok(Some(output)) if repl => {
|
||||
acc.push(Ok(output.to_repl()))
|
||||
},
|
||||
Ok(Some(output)) if repl => acc.push(Ok(output.to_repl())),
|
||||
Ok(_) => (),
|
||||
Err(error) => {
|
||||
acc.push(Err(error.msg));
|
||||
@ -200,11 +190,7 @@ impl<'a> State<'a> {
|
||||
ret = Some(prim);
|
||||
}
|
||||
}
|
||||
Ok(if let Some(ret) = ret {
|
||||
ret
|
||||
} else {
|
||||
self.expression(Expression::unit())?
|
||||
})
|
||||
Ok(if let Some(ret) = ret { ret } else { self.expression(Expression::unit())? })
|
||||
}
|
||||
|
||||
fn statement(&mut self, stmt: Statement) -> EvalResult<Option<MemoryValue>> {
|
||||
@ -214,7 +200,7 @@ impl<'a> State<'a> {
|
||||
let evaluated = self.expression(expr)?;
|
||||
self.environments.insert(id.into(), evaluated.into());
|
||||
Ok(None)
|
||||
},
|
||||
}
|
||||
Statement::Expression(expr) => {
|
||||
let evaluated = self.expression(expr)?;
|
||||
Ok(Some(evaluated.into()))
|
||||
@ -225,57 +211,74 @@ impl<'a> State<'a> {
|
||||
fn expression(&mut self, expression: Expression) -> EvalResult<Primitive> {
|
||||
Ok(match expression {
|
||||
Expression::Literal(lit) => Primitive::Literal(lit),
|
||||
Expression::Tuple(items) => Primitive::Tuple(items.into_iter().map(|expr| self.expression(expr)).collect::<EvalResult<Vec<Primitive>>>()?),
|
||||
Expression::Tuple(items) => Primitive::Tuple(
|
||||
items
|
||||
.into_iter()
|
||||
.map(|expr| self.expression(expr))
|
||||
.collect::<EvalResult<Vec<Primitive>>>()?,
|
||||
),
|
||||
Expression::Lookup(kind) => match kind {
|
||||
Lookup::Function(ref id) => {
|
||||
let mem = id.into();
|
||||
match self.environments.lookup(&mem) {
|
||||
// This just checks that the function exists in "memory" by ID, we don't
|
||||
// actually retrieve it until `apply_function()`
|
||||
Some(MemoryValue::Function(_)) => Primitive::Callable(Callable::UserDefined(id.clone())),
|
||||
Some(MemoryValue::Function(_)) => {
|
||||
Primitive::Callable(Callable::UserDefined(id.clone()))
|
||||
}
|
||||
x => return Err(format!("Function not found for id: {} : {:?}", id, x).into()),
|
||||
}
|
||||
},
|
||||
}
|
||||
Lookup::Param(n) => {
|
||||
let mem = n.into();
|
||||
match self.environments.lookup(&mem) {
|
||||
Some(MemoryValue::Primitive(prim)) => prim.clone(),
|
||||
e => return Err(format!("Param lookup error, got {:?}", e).into()),
|
||||
}
|
||||
},
|
||||
}
|
||||
Lookup::LocalVar(ref id) | Lookup::GlobalVar(ref id) => {
|
||||
let mem = id.into();
|
||||
match self.environments.lookup(&mem) {
|
||||
Some(MemoryValue::Primitive(expr)) => expr.clone(),
|
||||
_ => return Err(format!("Nothing found for local/gloval variable lookup {}", id).into()),
|
||||
_ => {
|
||||
return Err(
|
||||
format!("Nothing found for local/gloval variable lookup {}", id).into()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
Expression::Assign { ref lval, box rval } => {
|
||||
let mem = lval.into();
|
||||
let evaluated = self.expression(rval)?;
|
||||
self.environments.insert(mem, MemoryValue::Primitive(evaluated));
|
||||
Primitive::unit()
|
||||
},
|
||||
}
|
||||
Expression::Call { box f, args } => self.call_expression(f, args)?,
|
||||
Expression::Callable(Callable::DataConstructor { type_id, arity, tag }) if arity == 0 => Primitive::Object {
|
||||
type_id, tag, items: vec![]
|
||||
},
|
||||
Expression::Callable(Callable::DataConstructor { type_id, arity, tag }) if arity == 0 => {
|
||||
Primitive::Object { type_id, tag, items: vec![] }
|
||||
}
|
||||
Expression::Callable(func) => Primitive::Callable(func),
|
||||
Expression::Conditional { box cond, then_clause, else_clause } => {
|
||||
let cond = self.expression(cond)?;
|
||||
match cond {
|
||||
Primitive::Literal(Literal::Bool(true)) => self.block(then_clause)?,
|
||||
Primitive::Literal(Literal::Bool(false)) => self.block(else_clause)?,
|
||||
v => return Err(format!("Non-boolean value {:?} in if-statement", v).into())
|
||||
v => return Err(format!("Non-boolean value {:?} in if-statement", v).into()),
|
||||
}
|
||||
}
|
||||
Expression::CaseMatch { box cond, alternatives } => {
|
||||
self.case_match_expression(cond, alternatives)?
|
||||
}
|
||||
},
|
||||
Expression::CaseMatch { box cond, alternatives } => self.case_match_expression(cond, alternatives)?,
|
||||
Expression::ReductionError(e) => return Err(e.into()),
|
||||
})
|
||||
}
|
||||
|
||||
fn case_match_expression(&mut self, cond: Expression, alternatives: Vec<Alternative>) -> EvalResult<Primitive> {
|
||||
fn case_match_expression(
|
||||
&mut self,
|
||||
cond: Expression,
|
||||
alternatives: Vec<Alternative>,
|
||||
) -> EvalResult<Primitive> {
|
||||
fn matches(scrut: &Primitive, pat: &Pattern, scope: &mut ScopeStack<Memory, MemoryValue>) -> bool {
|
||||
match pat {
|
||||
Pattern::Ignored => true,
|
||||
@ -283,26 +286,35 @@ impl<'a> State<'a> {
|
||||
let mem = def_id.into();
|
||||
scope.insert(mem, MemoryValue::Primitive(scrut.clone())); //TODO make sure this doesn't cause problems with nesting
|
||||
true
|
||||
},
|
||||
Pattern::Literal(pat_literal) => if let Primitive::Literal(scrut_literal) = scrut {
|
||||
}
|
||||
Pattern::Literal(pat_literal) => {
|
||||
if let Primitive::Literal(scrut_literal) = scrut {
|
||||
pat_literal == scrut_literal
|
||||
} else {
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
Pattern::Tuple { subpatterns, tag } => match tag {
|
||||
None => match scrut {
|
||||
Primitive::Tuple(items) if items.len() == subpatterns.len() =>
|
||||
items.iter().zip(subpatterns.iter()).all(|(item, subpat)| matches(item, subpat, scope)),
|
||||
_ => false //TODO should be a type error
|
||||
Primitive::Tuple(items) if items.len() == subpatterns.len() => items
|
||||
.iter()
|
||||
.zip(subpatterns.iter())
|
||||
.all(|(item, subpat)| matches(item, subpat, scope)),
|
||||
_ => false, //TODO should be a type error
|
||||
},
|
||||
Some(pattern_tag) => match scrut {
|
||||
//TODO should test type_ids for runtime type checking, once those work
|
||||
Primitive::Object { tag, items, .. } if tag == pattern_tag && items.len() == subpatterns.len() => {
|
||||
items.iter().zip(subpatterns.iter()).all(|(item, subpat)| matches(item, subpat, scope))
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
Primitive::Object { tag, items, .. }
|
||||
if tag == pattern_tag && items.len() == subpatterns.len() =>
|
||||
{
|
||||
items
|
||||
.iter()
|
||||
.zip(subpatterns.iter())
|
||||
.all(|(item, subpat)| matches(item, subpat, scope))
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
let cond = self.expression(cond)?;
|
||||
@ -310,11 +322,9 @@ impl<'a> State<'a> {
|
||||
for alt in alternatives.into_iter() {
|
||||
let mut new_scope = self.environments.new_scope(None);
|
||||
if matches(&cond, &alt.pattern, &mut new_scope) {
|
||||
let mut new_state = State {
|
||||
environments: new_scope
|
||||
};
|
||||
let mut new_state = State { environments: new_scope };
|
||||
|
||||
return new_state.block(alt.item)
|
||||
return new_state.block(alt.item);
|
||||
}
|
||||
}
|
||||
Err("No valid match in match expression".into())
|
||||
@ -333,30 +343,36 @@ impl<'a> State<'a> {
|
||||
Some(MemoryValue::Function(FunctionDefinition { body })) => {
|
||||
let body = body.clone(); //TODO ideally this clone would not happen
|
||||
self.apply_function(body, args)
|
||||
},
|
||||
e => Err(format!("Error looking up function with id {}: {:?}", def_id, e).into())
|
||||
}
|
||||
},
|
||||
e => Err(format!("Error looking up function with id {}: {:?}", def_id, e).into()),
|
||||
}
|
||||
}
|
||||
Callable::Lambda { arity, body } => {
|
||||
if arity as usize != args.len() {
|
||||
return Err(format!("Lambda expression requries {} arguments, only {} provided", arity, args.len()).into());
|
||||
return Err(format!(
|
||||
"Lambda expression requries {} arguments, only {} provided",
|
||||
arity,
|
||||
args.len()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
self.apply_function(body, args)
|
||||
}
|
||||
Callable::DataConstructor { type_id, arity, tag } => {
|
||||
if arity as usize != args.len() {
|
||||
return Err(format!("Constructor expression requries {} arguments, only {} provided", arity, args.len()).into());
|
||||
return Err(format!(
|
||||
"Constructor expression requries {} arguments, only {} provided",
|
||||
arity,
|
||||
args.len()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let mut evaluated_args: Vec<Primitive> = vec![];
|
||||
for arg in args.into_iter() {
|
||||
evaluated_args.push(self.expression(arg)?);
|
||||
}
|
||||
Ok(Primitive::Object {
|
||||
type_id,
|
||||
tag,
|
||||
items: evaluated_args
|
||||
})
|
||||
Ok(Primitive::Object { type_id, tag, items: evaluated_args })
|
||||
}
|
||||
Callable::RecordConstructor { type_id: _, tag: _ } => {
|
||||
unimplemented!()
|
||||
@ -381,16 +397,16 @@ impl<'a> State<'a> {
|
||||
(IOPrint, &[ref anything]) => {
|
||||
print!("{}", anything.to_repl());
|
||||
Primitive::Tuple(vec![])
|
||||
},
|
||||
}
|
||||
(IOPrintLn, &[ref anything]) => {
|
||||
print!("{}", anything.to_repl());
|
||||
Primitive::Tuple(vec![])
|
||||
},
|
||||
}
|
||||
(IOGetLine, &[]) => {
|
||||
let mut buf = String::new();
|
||||
std::io::stdin().read_line(&mut buf).expect("Error readling line in 'getline'");
|
||||
StringLit(Rc::new(buf.trim().to_string())).into()
|
||||
},
|
||||
}
|
||||
/* Binops */
|
||||
(binop, &[ref lhs, ref rhs]) => match (binop, lhs, rhs) {
|
||||
// TODO need a better way of handling these literals
|
||||
@ -398,15 +414,19 @@ impl<'a> State<'a> {
|
||||
(Add, Lit(Int(l)), Lit(Int(r))) => Int(l + r).into(),
|
||||
(Add, Lit(Nat(l)), Lit(Int(r))) => Int((*l as i64) + (*r as i64)).into(),
|
||||
(Add, Lit(Int(l)), Lit(Nat(r))) => Int((*l as i64) + (*r as i64)).into(),
|
||||
(Concatenate, Lit(StringLit(ref s1)), Lit(StringLit(ref s2))) => StringLit(Rc::new(format!("{}{}", s1, s2))).into(),
|
||||
(Concatenate, Lit(StringLit(ref s1)), Lit(StringLit(ref s2))) => {
|
||||
StringLit(Rc::new(format!("{}{}", s1, s2))).into()
|
||||
}
|
||||
(Subtract, Lit(Nat(l)), Lit(Nat(r))) => Nat(l - r).into(),
|
||||
(Multiply, Lit(Nat(l)), Lit(Nat(r))) => Nat(l * r).into(),
|
||||
(Divide, Lit(Nat(l)), Lit(Nat(r))) => Float((*l as f64)/ (*r as f64)).into(),
|
||||
(Quotient, Lit(Nat(l)), Lit(Nat(r))) => if *r == 0 {
|
||||
(Divide, Lit(Nat(l)), Lit(Nat(r))) => Float((*l as f64) / (*r as f64)).into(),
|
||||
(Quotient, Lit(Nat(l)), Lit(Nat(r))) => {
|
||||
if *r == 0 {
|
||||
return Err("Divide-by-zero error".into());
|
||||
} else {
|
||||
Nat(l / r).into()
|
||||
},
|
||||
}
|
||||
}
|
||||
(Modulo, Lit(Nat(l)), Lit(Nat(r))) => Nat(l % r).into(),
|
||||
(Exponentiation, Lit(Nat(l)), Lit(Nat(r))) => Nat(l ^ r).into(),
|
||||
(BitwiseAnd, Lit(Nat(l)), Lit(Nat(r))) => Nat(l & r).into(),
|
||||
@ -435,7 +455,9 @@ impl<'a> State<'a> {
|
||||
(GreaterThanOrEqual, Lit(Int(l)), Lit(Int(r))) => Bool(l >= r).into(),
|
||||
(GreaterThanOrEqual, Lit(Float(l)), Lit(Float(r))) => Bool(l >= r).into(),
|
||||
|
||||
(binop, lhs, rhs) => return Err(format!("Invalid binop expression {:?} {:?} {:?}", lhs, binop, rhs).into()),
|
||||
(binop, lhs, rhs) => {
|
||||
return Err(format!("Invalid binop expression {:?} {:?} {:?}", lhs, binop, rhs).into())
|
||||
}
|
||||
},
|
||||
(prefix, &[ref arg]) => match (prefix, arg) {
|
||||
(BooleanNot, Lit(Bool(true))) => Bool(false),
|
||||
@ -445,8 +467,9 @@ impl<'a> State<'a> {
|
||||
(Negate, Lit(Float(f))) => Float(-(*f as f64)),
|
||||
(Increment, Lit(Int(n))) => Int(*n),
|
||||
(Increment, Lit(Nat(n))) => Nat(*n),
|
||||
_ => return Err("No valid prefix op".into())
|
||||
}.into(),
|
||||
_ => return Err("No valid prefix op".into()),
|
||||
}
|
||||
.into(),
|
||||
(x, args) => return Err(format!("bad or unimplemented builtin {:?} | {:?}", x, args).into()),
|
||||
})
|
||||
}
|
||||
@ -457,9 +480,7 @@ impl<'a> State<'a> {
|
||||
evaluated_args.push(self.expression(arg)?);
|
||||
}
|
||||
|
||||
let mut frame_state = State {
|
||||
environments: self.environments.new_scope(None)
|
||||
};
|
||||
let mut frame_state = State { environments: self.environments.new_scope(None) };
|
||||
|
||||
for (n, evaled) in evaluated_args.into_iter().enumerate() {
|
||||
let n = n as u8;
|
||||
@ -470,4 +491,3 @@ impl<'a> State<'a> {
|
||||
frame_state.block(body)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
#![cfg(test)]
|
||||
use test_case::test_case;
|
||||
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::tree_walk_eval::State;
|
||||
use crate::{symbol_table::SymbolTable, tree_walk_eval::State};
|
||||
|
||||
fn evaluate_input(input: &str) -> Result<String, String> {
|
||||
let ast = crate::util::quick_ast(input);
|
||||
@ -89,7 +88,7 @@ fn basic_if_statement() {
|
||||
|
||||
#[test]
|
||||
fn basic_patterns_1() {
|
||||
let source =r#"
|
||||
let source = r#"
|
||||
let x = 10
|
||||
let a = if x is 10 then { 255 } else { 256 }
|
||||
let b = if 23 is 99 then { 255 } else { 256 }
|
||||
@ -106,14 +105,16 @@ let d = if "xxx" is "yyy" then { 20 } else { 30 }
|
||||
#[test_case("cyrus", "4")]
|
||||
fn basic_patterns_2(input: &str, expected: &str) {
|
||||
let mut source = format!(r#"let x = "{}""#, input);
|
||||
source.push_str(r#"
|
||||
source.push_str(
|
||||
r#"
|
||||
if x {
|
||||
is "sanchez" then 1
|
||||
is "mouri" then 2
|
||||
is "hella" then 3
|
||||
is _ then 4
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
eval_assert(&source, expected);
|
||||
}
|
||||
|
||||
@ -121,12 +122,14 @@ if x {
|
||||
#[test_case(r#"(99, "panda", false, -2.45)"#, r#""maybe""#)]
|
||||
fn tuple_patterns(input: &str, expected: &str) {
|
||||
let mut source = format!("let x = {}", input);
|
||||
source.push_str(r#"
|
||||
source.push_str(
|
||||
r#"
|
||||
if x {
|
||||
is (45, "pablo", _, 28.4) then "no"
|
||||
is (_, "panda", _, 2.2) then "yes"
|
||||
is _ then "maybe"
|
||||
}"#);
|
||||
}"#,
|
||||
);
|
||||
|
||||
eval_assert(&source, expected);
|
||||
}
|
||||
@ -230,7 +233,6 @@ if (1, 2) {
|
||||
eval_assert(source, "2");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn tuple_pattern_2() {
|
||||
let source = r#"
|
||||
@ -264,7 +266,6 @@ if (1, 5) {
|
||||
eval_assert(source, "5");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn prim_obj_pattern() {
|
||||
let source = r#"
|
||||
@ -305,7 +306,7 @@ let y = \(m, n, o) { m + n + o }(1,2,3)
|
||||
(x, y)
|
||||
"#;
|
||||
|
||||
eval_assert(source, r"(10, 6)");
|
||||
eval_assert(source, r"(10, 6)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -318,7 +319,7 @@ milta()(10)
|
||||
"#;
|
||||
|
||||
eval_assert(source, "43");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_all() {
|
||||
|
Loading…
Reference in New Issue
Block a user