Your example doesn't show using a type parameter, which is what makes a GADT a GADT. As far as I can see your example is just an ADT.
[1] https://wiki.haskell.org/Existential_type#Dynamic_dispatch_m...
The other subtle thing with GADTs is that the type information must flow back from the matched case to the type parameter: if you match an Ast<R> and get Mul, you know that R=Int. And that part is impossible to do fully in a language that doesn't have higher-kinded types, because you not only need to be able to know that you can pass an Int where an R was expected, but also that you can pass an Ast<Int> where an Ast<R> is expected, or return a List<Int> where a List<R> is expected, or....