Verilog: Output propagation
Yes, that is correct behavior of event driven simulation. But it can broken down even further. You update the value of a and g2 could update the value of e. And then either b gets updated, or g3 updates the value of f. Only the statements within the  begin/end block have guaranteed ordering so that a must be updated before b, and b must be updated before c.
And yes, even with a simple nand g4(o,o,i); you can create an oscillation when i goes to 1. With a 0 output delay your simulation will hang. Most simulators catch this by placing a limit on the number of evaluations that can happen without time advancing. But with a non-zero output delay, o will oscillate until something else stops the simulation. The simulator has no way of knowing what the intent was; it does what you tell it to do.