use operator::Operator; use region::Located; use std::fmt; #[derive(Clone, Debug, PartialEq)] pub enum Expr { // Literals Int(i64), Frac(i64, i64), Approx(f64), EmptyStr, Str(String), Char(char), List(Vec>), EmptyList, // Lookups Var(Ident), InterpolatedStr(Vec<(String, Located)>, String), // Pattern Matching Case(Box>, Vec<(Located, Located)>), Closure(Vec>, Box>), Assign(Vec<(Located, Located)>, Box>), // Application Apply(Box>, Vec>), ApplyVariant(VariantName, Option>>), // Product Types EmptyRecord, // Sugar If(Box>, Box>, Box>), Operator(Box>, Located, Box>), } /// A variant name, possibly fully-qualified with a module name /// e.g. (Result.Ok) /// Parameterized on a phantom marker for whether it has been canonicalized #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum VariantName { Unqualified(String), Qualified(String, String), } /// An identifier, possibly fully-qualified with a module name /// e.g. (Http.Request from http) /// Parameterized on a phantom marker for whether it has been canonicalized #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum Ident { Unqualified(String), Qualified(String, String), } impl Ident { pub fn is_qualified(&self) -> bool { match &self { &Ident::Unqualified(_) => false, &Ident::Qualified(_, _) => true, } } pub fn name(self) -> String { match self { Ident::Unqualified(name) => name, Ident::Qualified(_, name) => name } } } impl fmt::Display for Ident { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Ident::Unqualified(name) => { write!(f, "{}", name) }, Ident::Qualified(path, name) => { write!(f, "{}.{}", path, name) } } } } impl fmt::Display for VariantName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { VariantName::Unqualified(name) => { write!(f, "{}", name) }, VariantName::Qualified(path, name) => { write!(f, "{}.{}", path, name) } } } } #[derive(Clone, Debug, PartialEq)] pub enum Pattern { Identifier(String), Variant(Located, Option>>), Integer(i64), Fraction(i64, i64), ExactString(String), EmptyRecordLiteral, Underscore, } impl Expr { pub fn walk(self: &Expr, transform: &F) -> Expr where F: Fn(&Expr) -> Expr { use self::Expr::*; let transformed = transform(self); match transformed { Int(_) | Frac(_, _) | Approx(_) | EmptyStr | Str(_) | Char(_) | Var(_) | EmptyRecord | InterpolatedStr(_, _) | EmptyList => transformed, List(elems) => { let new_elems = elems.into_iter() .map(|loc_elem| loc_elem.with_value(loc_elem.value.walk(transform))) .collect(); List(new_elems) } Assign(assignments, loc_ret) => { Assign( assignments.into_iter().map(|(pattern, loc_expr)| (pattern, loc_expr.with_value(loc_expr.value.walk(transform))) ).collect(), Box::new(loc_ret.with_value(loc_ret.value.walk(transform))) ) }, Apply(fn_expr, args) => { Apply( Box::new(fn_expr.with_value(fn_expr.value.walk(transform))), args.into_iter().map(|arg| arg.with_value(arg.value.walk(transform))).collect() ) }, Closure(patterns, body) => Closure(patterns, Box::new(body.with_value(body.value.walk(transform)))), ApplyVariant(_, None) => transformed, ApplyVariant(name, Some(args)) => { ApplyVariant( name, Some( args.into_iter().map(|arg| arg.with_value(arg.value.walk(transform))).collect()) ) }, If(condition, if_true, if_false) => { If( Box::new(condition.with_value(condition.value.walk(transform))), Box::new(if_true.with_value(if_true.value.walk(transform))), Box::new(if_false.with_value(if_false.value.walk(transform))) ) }, Case(condition, branches) => { Case( Box::new(condition.with_value(condition.value.walk(transform))), branches.into_iter().map(|( pattern, body )| ( pattern, body.with_value(body.value.walk(transform)) ) ).collect() ) }, Operator(loc_left, loc_op, loc_right) => { Operator( Box::new(loc_left.with_value(loc_left.value.walk(transform))), loc_op, Box::new(loc_right.with_value(loc_right.value.walk(transform))) ) } } } }