Cubix, 238 234 217 bytes
Saved 4 bytes thanks to ETHProductions
........................v.<...../#\...vt0....<!w.IO'^ou>'=o;\$o;\s+'q:q\.;sq\Uq:OI;.....q$s.su-.(Uq^.u1.s'*s.../....sqq.'1r.!tu:sqq.*..../;\....O?^.*sr/\#;;Ur\;/...........#(U-/;U;q....\r;\sU.........Ut!\.;.;*;r1-!@;^
Try it online!
Try it here
Explanation
First of all, the expanded cube (currently 7x7x7) looks like this:
. . . . . . .
. . . . . . .
. . . . . . .
. . . v . < .
. . . . / # \
. . . v t 0 .
. . . < ! w .
I O ' ^ o u > ' = o ; \ $ o ; \ s + ' q : q \ . ; s q \
U q : O I ; . . . . . q $ s . s u - . ( U q ^ . u 1 . s
' * s . . . / . . . . s q q . ' 1 r . ! t u : s q q . *
. . . . / ; \ . . . . O ? ^ . * s r / \ # ; ; U r \ ; /
. . . . . . . . . . . # ( U - / ; U ; q . . . . \ r ; \
s U . . . . . . . . . U t ! \ . ; . ; * ; r 1 - ! @ ; ^
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
The code consists of 8 steps, with two loops. I'll go over the code part by part.
Step 1 (A^B)
I O ' ^ o u >
U q : O I ; .
This block of code is on the left-most side, so the IP (instruction pointer) starts at the top-left. It executes the following commands (excluding IP control codes).
IO'^o;IO:q # Explanation
I # Push the first input (A) to the stack
O # Output the top element of the stack
'^ # Push the character "^" to the stack
o # And output that
; # Then remove it from the stack
I # Push the second input (B) to the stack
O # Output that and
: # Duplicate and
q # Send one copy to the bottom of the stack
Now, the stack looks like this: B, A, B
Step 2 (prepare for print loop)
The print loop takes 3 arguments (the top 3 elements on the stack): A, B and C. A is an integer that is printed, B is the separator (character code) and C is the amount of times to repeat. Luckily, the loop also takes care of the requirement that the resulting string should end in A, not B.
We want to repeat A* exactly B times, with separator *. Note that the stack starts as B, A, B.
'*s # Explanation
'* # Push the character code of *
s # Swap the top two elements
Step 3/6/8 (the print loop)
The basic principle of the loop is that there's a reflector out of the loop, but there's a ! before that, so the IP will only leave the loop if the top element of the stack is falsy (0).
. . . . / # \
<=s . . . . t 0 . <= s
. . . . ! w .
=> ' = o ; \ $ o =>
. . . . q $ s
. . . . s q q
. . . . O ? ^
. . . . # ( .
. . . . U t .
The arrows show how the IP enters and exits the loop. I removed some surrounding codes that are not used by the loop. As you can see, the IP first comes across four instructions before entering the loop.
'=o; #
'= # Push the character code of =
o # Output it as a character
; # Remove it from the stack.
Since the character code is removed again, we reach the loop with the exact same stack as we entered this part. Now, the IP finds a mirror, reflecting the IP down. A few instructions later, there's a U, which turns the IP around, and at the top there are again 2 mirrors, turning the IP around once more, and just before it would reflect out of the loop, there's a !, which makes the IP skip the next instruction if the top item on the stack is truthy (not zero).
The first thing the loop does is move the top element on the stack (C, the amount of iterations) to the bottom (q). Then it swaps the top two elements (s, stack now looks like C, ..., B, A). Then it outputs the top of the stack as a number (O, top of the stack is A). Then it moves the bottom element to the top (#t, stack now looks like ..., B, A, C). It then decreases C by one and compares that value to zero ((?).
Since C is always positive or zero, we only have to consider two cases. If C is zero (i.e. this is the last iteration of the loop), the IP continues moving upward. If C is positive, it continues pointing right, but is then redirected upwards (^). In both cases, the top of the stack (C) is sent to the bottom again (Stack now looks like C-1, ..., B, A).
In the case of C=0, the IP hops over some instructions and then executes a w, which is a sidestep to the right. At that point the two paths merge again. The stack ends up like (C-1, ..., B, A), which is not exactly the same as in the other case, but the IP will exit the loop anyways, so it's not that important (as long as C-1 is on the bottom of the stack).
In the other case, the IP swaps the top elements (s, Stack: C-1, ..., A, B) and then outputs the top element.
The two paths have merged, and we move the bottom element of the stack to the top (C-1) and compare that to 0 (#t!). If this is 0, the next instruction is executed, reflecting the IP left. If this is not zero, the IP skips the next instruction, continuing the loop. Note that in this case, the stack is ..., A, B, C-1 again, so everything lines up.
Unfortunately, there's a o on the way out, which would output 0 as a character code, which is not what we want. As such, we have to place a $, which makes the IP skip the next instruction. However, if the IP reaches this $ from the bottom (which it will in the case of C=0), it will skip the sidestep instruction (which would break the loop). Therefore, we have to place another $ before the $ to skip the "skip next instruction" when coming from the south.
Step 4 (differentiating the IPs)
Since we use the above loop multiple times, but they all end up out of the loop in the same spot, we have to differentiate between multiple runs. First, we distinguishing between the * and + part, and if it's the + version, we have to distinguish between the number that is repeated. If it's 1, we just did the last iteration and we can stop the program.
Here's what it looks like on the cube:
=> ; \ . . . . . . . . . .
. . s . . . . . . . . . .
. . ' . . . . . . . . . .
. . * . . . . . . . . . .
U - / . . . . . . . . . .
! \ . ; . ; * ; r 1 - ! @ 2>
1
v
The fat arrow => shows where the IP enters this part, and the 1> arrow shows where it exits the first time, the 2> arrow shows where it exits the second time, and the third time, the program stops.
First comparison
;s'*-!
; # Delete top element (`0`)
s # Swap the top two elements (Stack: A, B)
'* # Push the character code of *
- # Subtract the top two elements and push
# that to the stack (Stack: A, B, *, B-*)
! # Compare against 0.
# If B == *:
# Execute next instruction
# Else:
# Skip next instruction
The next instruction sends the IP down, causing it to exit at arrow 1> (Go to step 5).
Second comparison
We are now going to compare A against 1 in roughly the same way. The stack initially looks like this: A, B, *, B-*
;;*;r1-!
;; # Delete top two elements
* # Push multiplication of top two elements
; # and remove it again (these two instructions
# also part of another
# loop, and this way we
# can safely pass through)
r # Rotate top three elements (Stack: B, ..., A)
1- # Subtract the top element by 1 (Stack B, ..., A, 1, A-1)
! # Compare against 0.
# If A == 1:
# Execute next instruction
# Else:
# Skip next instruction
The next instruction @ ends the program. If that was skipped, the IP continues going right.
Step 5 (preparing for "A+" (repeat A^(B-1)))
Sadly, Cubix doesn't have a power operator, so we need another loop. However, we need to clean up the stack first, which now contains B, A, B, *, B-*.
Cleaning up
;;;s1-rr; # Explanation
;;; # Remove top 3 elements (Stack: B, A)
s # Swap top 2 elements (Stack: A, B)
1- # Push 1 and subtract top 2 elements (Stack: A, B, 1, B-1)
rr # Rotate twice (Stack: A, 1, B-1, B)
; # Remove top element (Stack: A, 1, B-1)
Calculating A^(B-1)
Another loop which works roughly the same as the print loop, but it's quite a bit shorter. The bottom part is moved to the left-most side of the cube in the final version, but it's moved back here for clarity and brevity.
( U
! t
=> \ # =>
q .
s> * ; s>
U s
The fat arrows (=>) show where the IP enters and exits the loop. The s> arrows show where the IP can safely go through some instructions.
This loop doesn't need any conditional things inside it, so it's pretty simple. Note that the stack initially is A, 1, B-1. The loop body is the following:
q*s;.#t( # Explanation
q # Move the top element to the bottom
* # Multiply the top two elements
s # Shift the top two elements
; # Delete the top element
. # No-op
#t # Move the bottom element to the top
( # Decrease it
The resulting stack is A, A^(B-1), 0, 3.
Cleaning up the stack (again)
Now we need to get to the print loop again, with the top of the stack containing A, +, A^(B-1). To do this, we execute the following.
;;s:qq:q'+s # Explanation
;; # Delete top two elements (Stack: A, A^(B-1))
s # Shift elements
:qq # Duplicate the top element and send both to the
# bottom (Stack: A, A, A^(B-1))
:q # Duplicate the top element and send it to the
# bottom (Stack: A^(B-1), A, A, A^(B-1))
'+ # Push the +
s # Shift top two elements
The stack now contains: A^(B-1), A, A, +, A^(B-1), so we can enter the print loop.
Step 7 (preparing for last loop)
Explanation in progress...
='s to the left of any3's. – R. Kap yesterday2^3a valid input format? Or does it have to be space/comma/linefeed-separated or something? – Martin Ender♦ 22 hours ago