You're looking to write (or find, if someone else has written) an input buffer. Basically when the player presses an action, instead of doing it immediately, you put it in a queue-like list with a "timeout" associated with it. Then in _process or wherever you're doing your movement, loop through the queue. decrement the timeouts, get rid of any actions that have timed out, and when you get to an action that you can perform in your current state, you flush it and everything in front of it and then perform that action.
Say your character can punch in the air but can only jump on the ground. The player jumps and hits "jump" again just before touching the ground. "jump" goes in the queue. It isn't usable right away, because you're airborne, but within the timeout window the character becomes grounded.
Your player does the same thing again, only this time between hitting "jump" and touching the ground, they hit "punch". Punch can be done while airborne, so the loop sees it, flushes the jump in front of it, and performs the aerial punch. The second jump doesn't happen, because that's the opposite the order of inputs and would feel weird. But if the player hit "jump" again while they were punching, "jump" would go back in the queue and end up being performed when they landed.
(Edit: the flushing isn't always necessary. In a game where punching doesn't affect your movement at all, less necessary. It's a design decision to make.)
You can use this same pattern for chaining attacks and other actions as well.