in reply to [SOLVED] Pack/unpack - understanding the '@' and '.' templactes
So, the "@" repositions the pointer to which character will next be consumed from the expression string. You may pass a number to it.. so:
Now, when it's inside a group, it doesn't move the pointer to the start of the expression, but back to where the current group started matching:
say unpack 'aaaaa@0aa', 'ABCDEFGH'; # prints ABCDEAB
Now you'll notice I was supplying a 0 parameter to @, which means back to the very start of the string or group. But if you don't supply a number, 1 is used as a default.
say unpack 'aa(aaa@0)aa', 'ABCDEFGH'; # prints ABCDECD
So in your first example:
say unpack 'aa(aaa@)aa', 'ABCDEFGH'; # prints ABCDEDE
say unpack 'aa(aaa@1)aa', 'ABCDEFGH'; # prints ABCDEDE
say unpack '(C/xa@)3', "\003\003\003abcdef"; # prints 'bcd'
C reads in the first byte (which is 3) which then is used as a length by /x to move over 3 bytes in the input expression where the 'a' reads the next character yielding "b". The @ having no numeric modifier, and being constrained in a group moves back to 1 byte from the starting point of where the group was anchored. Since the group is currently anchored on the very first character, the new starting point will be the second byte.
And the whole thing repeats but now anchored on this new starting point. The next time @ is met, it will move back to 1 byte from the current starting point... ie the 3rd byte.
In this way, the 3 offset bytes at the start of the input expression can each be scanned. We jump forward to process what each one references, and then we jump back to process the next offset.
Now to the "." character. It's kind of tricky. Essentially what it does is give you your current pointer position within the expression. So:
Now we can combine this with the '/' operator! Which will consume this number instead of sending it to the output:
say unpack 'aa.', 'ABCDEFGH'; # prints AB2
say unpack 'aaaa.', 'ABCDEFGH'; # prints ABCD4
What happened? The first two 'a' print AB, the . returns '2' (as in the above example) but instead of being printed is consumed by the '/x' which moves the input pointer over by this 2 characters. Where the next two 'a' print EF.
When used inside of a group, by default the '.' character will return to you the offset from the start of the group! But using the '*' modifier, we can escape the group and get it to act like it's not inside a group and return the absolute position from the start of the string:
say unpack 'aa./xaa', 'ABCDEFGH'; # prints ABEF
And this should give you a hint about the answer to your second question. To return to the absolute start of the string, use the ".*" construct to get your current position combined with "/X" to move backward that many characters:
say unpack 'aa.aa', 'ABCDEFGH'; # prints AB2CD
say unpack 'a(a.a)a', 'ABCDEFGH'; # prints AB1CD
say unpack 'a(a.*a)a', 'ABCDEFGH'; # prints AB2CD
say unpack 'aa(aa).*/Xaa', 'ABCDEFGH'; # prints ABCDAB
say unpack 'aa(aa.*/Xaa)', 'ABCDEFGH'; # prints ABCDAB