What you describe is a "non-regular language". It cannot be parsed with a regexp.
Ok, if you are willing to put a limit to the nesting level, technically you can do it with a regexp. But it will be ugly.
Here is how to parse your thing with a few (increasing) maximum nesting depths, if you can put the condition of not having @'s inside your tags:
no nesting: <@[^@]+@>
up to 1: <@[^@]+(<@[^@]+@>)?[^@]*@>
up to 2: <@[^@]+(<@[^@]+(<@[^@]+@>)?[^@]*@>)?[^@]*@>
up to 3: <@[^@]+(<@[^@]+(<@[^@]+(<@[^@]+@>)?[^@]*@>)?[^@]*@>)?[^@]*@>
...
If you cannot forbid lone @'s in your tags, you will have to replace every instance of [^@]
with something like this: (?:[^<@]|<[^@]|@[^>])
.
Just think about that and then think about extending your regex to parse up to 10 depth nesting.
Here, I will do it for you:
<@(?:[^<@]|<[^@]|@[^>])+(<@(?:[^<@]|<[^@]|@[^>])+(<@(?:[^<@]|<[^@]|@[^>])+(<@(?:[
^<@]|<[^@]|@[^>])+(<@(?:[^<@]|<[^@]|@[^>])+(<@(?:[^<@]|<[^@]|@[^>])+(<@(?:[^<@]|<
[^@]|@[^>])+(<@(?:[^<@]|<[^@]|@[^>])+(<@(?:[^<@]|<[^@]|@[^>])+(<@(?:[^<@]|<[^@]|@
[^>])+(<@(?:[^<@]|<[^@]|@[^>])+@>)?(?:[^<@]|<[^@]|@[^>])*@>)?(?:[^<@]|<[^@]|@[^>]
)*@>)?(?:[^<@]|<[^@]|@[^>])*@>)?(?:[^<@]|<[^@]|@[^>])*@>)?(?:[^<@]|<[^@]|@[^>])*@
>)?(?:[^<@]|<[^@]|@[^>])*@>)?(?:[^<@]|<[^@]|@[^>])*@>)?(?:[^<@]|<[^@]|@[^>])*@>)?
(?:[^<@]|<[^@]|@[^>])*@>)?(?:[^<@]|<[^@]|@[^>])*@>
What I hope my answer shows is that regexp are not the right tool to parse a language. A traditional lexer (tokenizer) and parser combination will do a much better job, be significantly faster, and will handle indefinite nesting.