inductive Expr
| Val : Int → Expr
| Var : String → Expr
| Add : Expr → Expr → Expr
| Mul : Expr → Expr → Expr

open Expr

def Expr.toString : Expr → String
| (Val n) := toString n
| (Var x) := x
| (Add f g) := "(" ++ Expr.toString f ++ " + " ++ Expr.toString g ++ ")"
| (Mul f g) := "(" ++ Expr.toString f ++ " * " ++ Expr.toString g ++ ")"

instance : HasToString Expr :=
⟨Expr.toString⟩

partial def addAux : Expr → Expr → Expr
| f         (Add (Val n) g) := addAux (Val n) (addAux f g)
| f         g               := Add f g

def add (a b : Expr) : Expr :=
addAux a b

def main (xs : List String) : IO UInt32 :=
do let x := Var "x";
   IO.println (add (Val 1) (Add (Mul (Val 2) x) x));
   pure 0
