Note that there are only so many things you can instantiate.
It is possible to construct a gate which is dual edge triggered (handy as a phase-frequency detector), and you could describe it with two IF RISING_EDGE(x) statements; this will generate an error, because the synthesizer only knows the logic primitives it was made for, and the flip-flops on the CPLD/FPGA are normal D-type (single clocked, async set/reset) so it thinks this is impossible.
Here's such a gate:

To construct such things, you have to describe it combinatorially -- that is, in terms of plain logic assignments, no events (rising/falling edges define sequential blocks). They'll be synthesized as such -- there are only a few feedback paths (usually adder carry) within a logic block, so the synthesizer must allocate several blocks, and the resulting gate incurs several propagation delays -- it's a terribly inefficient way to do it (but, yes, it does work, you can make a multi-clocked gate as such).
The other alternative is to try and synthesize it from primitives you
do have access to. Example:

This isn't quite the same but it is with one more flip-flop added at the end. Typically, the gates are pretty much for free and the flip-flop uses one logic block, so you end up with two instead of four propagation delays -- better performance.
All this is reflected in the pattern and type of HDL statements used. In short, an effective way to understand HDL is, knowing what code maps to what RTL components.
Which is also an effective way to go about programming as well, for example understanding the exact semantics that C uses to select machine instructions for your program, or how an object-oriented language is built.
It is an approach which doesn't lend itself so well to portability; once you get comfortable with one architecture, you're either going to end up applying the same lessons blindly to another architecture and screw it up, or you're going to study the new architecture to the same detail and continue to succeed just as well.

Tim