package com.acme.symbolic; import church.lang.operators.Relational.$$equal; import church.primitives.Objects; import static church.lang.operators.Arithmetic.*; import static church.lang.operators.Streams.$$encode; @SuppressWarnings("unchecked") public abstract class Expression<T> { private static final $$equal<String> $S0 = Objects::$equal; private static interface $I0<T> { public T eval(Expression<T> exp); } public interface Visitor<T, S> { default S get(Expression<T> receiver) { S result = receiver.accept(this); return result == null ? defaultImplementation(receiver) : result; } default S getOrDefault(Expression<T> receiver, S defaultValue) { S result = receiver.accept(this); return result == null ? defaultValue : result; } default S $const(T value) { return null; } default S var(String name) { return null; } default S app(String fun, Expression<T> arg) { return null; } default S sum(Expression<T> a, Expression<T> b) { return null; } default S neg(Expression<T> a) { return null; } default S sub(Expression<T> a, Expression<T> b) { return null; } default S prd(Expression<T> a, Expression<T> b) { return null; } default S div(Expression<T> a, Expression<T> b) { return null; } default S pow(Expression<T> a, T n) { return null; } default S defaultImplementation(Expression<T> e) { throw new UnsupportedOperationException(); } } public abstract <S> S accept(Visitor<T, S> visitor); public static <T> Expression<T> $const(T value) { return new Expression<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.$const(value); } }; } public static <T> Expression<T> var(String name) { return new Expression<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.var(name); } }; } public static <T> Expression<T> app(String fun, Expression<T> arg) { return new Expression<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.app(fun, arg); } }; } public static <T> Expression<T> sum(Expression<T> a, Expression<T> b) { return new Expression<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.sum(a, b); } }; } public static <T> Expression<T> neg(Expression<T> a) { return new Expression<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.neg(a); } }; } public static <T> Expression<T> sub(Expression<T> a, Expression<T> b) { return new Expression<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.sub(a, b); } }; } public static <T> Expression<T> prd(Expression<T> a, Expression<T> b) { return new Expression<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.prd(a, b); } }; } public static <T> Expression<T> div(Expression<T> a, Expression<T> b) { return new Expression<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.div(a, b); } }; } public static <T> Expression<T> pow(Expression<T> a, T n) { return new Expression<T>() { public <S> S accept(Visitor<T, S> visitor) { return visitor.pow(a, n); } }; } public static interface $Evaluate<T> { public T evaluate(church.lang.Functions.Function1<String, church.lang.Functions.Function1<T, T>> funNameToFunction, Expression<T> exp0, church.lang.Functions.Function1<String, T> varNameToValue); } public static <T> $Evaluate<T> evaluate($$sum<T> $L0, $$neg<T> $L1, $$sub<T> $L2, $$prd<T, T> $L3, $$div<T, T> $L4, $$pow<T, T> $L5) { return (funNameToFunction, exp0, varNameToValue) -> { $I0<T> eval = new $I0<T>() { public T eval(Expression<T> exp) { return new Visitor<T, T>() { public T $const(T value) { return value; } public T var(String name) { return varNameToValue.of(name); } public T app(String funName, Expression<T> a) { return funNameToFunction.of(funName).of(eval(a)); } public T sum(Expression<T> f, Expression<T> g) { return $L0.$sum(eval(f), eval(g)); } public T neg(Expression<T> f) { return $L1.$neg(eval(f)); } public T sub(Expression<T> f, Expression<T> g) { return $L2.$sub(eval(f), eval(g)); } public T prd(Expression<T> f, Expression<T> g) { return $L3.$prd(eval(f), eval(g)); } public T div(Expression<T> f, Expression<T> g) { return $L4.$div(eval(f), eval(g)); } public T pow(Expression<T> f, T n) { return $L5.$pow(eval(f), n); } } .get(exp); } }; return eval.eval(exp0); }; } public static interface $ExprEqual<T> { public boolean exprEqual(Expression<T> a, Expression<T> b); } public static <T> $ExprEqual<T> exprEqual($$equal<T> $L0) { return (a, b) -> new Visitor<T, Boolean>() { public Boolean $const(T v1) { return new Visitor<T, Boolean>() { public Boolean $const(T v2) { return $L0.$equal(v1, v2); } } .getOrDefault(b, false); } public Boolean var(String n1) { return new Visitor<T, Boolean>() { public Boolean var(String n2) { return $S0.$equal(n1, n2); } } .getOrDefault(b, false); } } .getOrDefault(a, false); } public static <T, U> $$encode<T, Expression<U>> $encode($$encode<T, U> $L0, $$encode<T, String> $L1) { return new $$encode<T, Expression<U>>() { public T $encode(T stream, Expression<U> $0) { return new Visitor<U, T>() { public T $const(U v) { return $L0.$encode(stream, v); } public T var(String name) { return $L1.$encode(stream, name); } public T app(String f, Expression<U> a) { return $L1.$encode($encode($L1.$encode($L1.$encode(stream, f), "("), a), ")"); } public T sum(Expression<U> a, Expression<U> b) { return $encode($L1.$encode($encode(stream, a), " + "), b); } public T neg(Expression<U> a) { return $encode($L1.$encode(stream, "-"), a); } public T sub(Expression<U> a, Expression<U> b) { return $encode($L1.$encode($encode(stream, a), " - "), b); } public T prd(Expression<U> a, Expression<U> b) { return $encode($L1.$encode($encode(stream, a), " * "), b); } public T div(Expression<U> a, Expression<U> b) { return $encode($L1.$encode($encode(stream, a), " / "), b); } public T pow(Expression<U> a, U b) { return $L0.$encode($L1.$encode($encode(stream, a), "^"), b); } } .get($0); } }; } public static <T> $$equal<Expression<T>> $equal($ExprEqual<T> $L0) { return (a, b) -> $L0.exprEqual(a, b); } public static <T> $Additive_identity<Expression<T>> additive_identity($Additive_identity<T> $L0) { return () -> $const($L0.additive_identity()); } public static <T> $Multiplicative_identity<Expression<T>> multiplicative_identity($Multiplicative_identity<T> $L0) { return () -> $const($L0.multiplicative_identity()); } public static <T> $$sum<Expression<T>> $sum($$equal<Expression<T>> $L0, $Additive_identity<Expression<T>> $L1) { return (a, b) -> $L0.$equal(a, $L1.additive_identity()) ? b : $L0.$equal(b, $L1.additive_identity()) ? a : sum(a, b); } public static <T> Expression<T> $neg(Expression<T> a) { return neg(a); } public static <T> $$sub<Expression<T>> $sub($$equal<Expression<T>> $L0, $Additive_identity<Expression<T>> $L1, $$neg<Expression<T>> $L2) { return (a, b) -> $L0.$equal(a, $L1.additive_identity()) ? $L2.$neg(b) : $L0.$equal(b, $L1.additive_identity()) ? a : sub(a, b); } public static <T> $$prd<Expression<T>, Expression<T>> $prd($$equal<Expression<T>> $L0, $Additive_identity<Expression<T>> $L1, $Multiplicative_identity<Expression<T>> $L2) { return (a, b) -> $L0.$equal(a, $L1.additive_identity()) ? $L1.additive_identity() : $L0.$equal(b, $L1.additive_identity()) ? $L1.additive_identity() : $L0.$equal(a, $L2.multiplicative_identity()) ? b : $L0.$equal(b, $L2.multiplicative_identity()) ? a : prd(a, b); } public static <T> $$div<Expression<T>, Expression<T>> $div($$equal<Expression<T>> $L0, $Additive_identity<Expression<T>> $L1, $Multiplicative_identity<Expression<T>> $L2) { return (a, b) -> $L0.$equal(a, $L1.additive_identity()) ? $L1.additive_identity() : $L0.$equal(b, $L2.multiplicative_identity()) ? a : div(a, b); } public static <T> $$pow<Expression<T>, T> $pow($$equal<Expression<T>> $L0, $Additive_identity<Expression<T>> $L1, $$equal<T> $L2, $Additive_identity<T> $L3, $Multiplicative_identity<Expression<T>> $L4, $Multiplicative_identity<T> $L5) { return (a, n) -> $L0.$equal(a, $L1.additive_identity()) ? $L1.additive_identity() : $L2.$equal(n, $L3.additive_identity()) ? $L4.multiplicative_identity() : $L2.$equal(n, $L5.multiplicative_identity()) ? a : pow(a, n); } }