[Question]
  [
Sometimes I make bad jokes... And a bad joke I like to make involves interpreting exclamation marks in sentences as the factorial sign.
# Task
Your task is to write a program that receives a sentence and applies the factorial joke to the sentence.
The "factorial joke" consists of looking for exclamation marks "!" and doing the factorial of whatever is to the left of it. If the thing to the left is an integer, then the usual factorial is used. If the thing to the left is a word (a sequence of characters in `[a-zA-Z]`, delimited by spaces), then we want to concatenate all of the subsequent prefixes of the word.
E.g. if the word was `abcd` then `abcd! = abcdabcaba`.
**However**, there is an exception, which is when the sentence contains a "1!" or "2!", because `1! = 1` and `2! = 2` and the joke doesn't really work. In these cases, your program can do whatever it wants EXCEPT applying the factorial joke or returning the same sentence.
# Input
A sentence with characters in the range `[a-zA-Z0-9 !]` with the restriction that only one exclamation mark "!" is present and to the right of an integer or a "word". This means you don't have to worry with things as `"abc4!"` as a word here is defined as a sequence of *alphabetical* characters delimited by spaces.
# Output
The same sentence, but with the factorial joke applied to the word or integer on the left of the exclamation mark.
# Test cases
You can find a [Python reference implementation](https://tio.run/##hVNNj5swEL3zKx5cFpQPhaRSq0jZWw8rVb20ag8IbRwYgrPERrZZkq72t6fjkKRRVamCA543896b8dAeXa3V4lNrTie5b7Vx2AtXB0FJFXb6hWKbLANAlgesYKdSlXSIozBKOGidMM4@C8cQJ3Ckr2VDsNkNyRGuEPEjVHmX/7jCzNPec0xWSAPP4YX@MGCEdMnsOUP8OnMcCqUXVS7uk2A4VxySCnE6xjwZcgBDrjMKUce@K6mojIbsShTete912hpdxplCpQ2UpzBCbcnzyFGa5ElwR2Sz5c3ZKM3ZnHUm9myJ/87Y6Chd5l6EDgW1Dj9E09FnY7QZPCnqLSs/PEx3Wqq4z5Yq/1u6IQaS8Ww8SZP/yJ/p7pRPjqzzAhnXRT8JjbYOribfa1FjAacxC6Oxh7UR2OhG2Mt5K185j/Dhcn5CL5SjEvMQV2w@QF/IobPYdLIpIWCV7vdCXepYVhiWFo6woUJ0liAdpEUr2E368cavVXNELZhaK/pHNL3GVGGolJuGQjwNkKJXMrBEipeL11iqLRr54pUuLmreHsHoXvzyYKFLwlY3FYpaNDzjLfFg2JS89v9N85TUEX6GKISlK/C9Jjv0xDfdcfERu853cvX3VfOopAv9pM2QmS6igBehNX5Lo/V6zf@Mv@czOV/1@aL8TgwZVfTmI@@YPOLt/Of5Y/LOVfcUp98) here
```
We lost the match 3 to 0! -> We lost the match 3 to 1
ora bolas! -> ora bolasbolabolbob
give me 4! -> give me 24
I wanted 2! give me 2 -> undefined
Let us build a snowman! -> Let us build a snowmansnowmasnowmsnowsnosns
We are late because it is past 17! -> We are late because it is past 355687428096000
I only have one! -> I only have oneono
I only have 1! -> undefined
Incredible! I have never seen anything like it -> IncredibleIncrediblIncredibIncrediIncredIncreIncrIncInI I have never seen anything like it
What an amazing code golf challenge this is! -> What an amazing code golf challenge this isi
So many test cases! -> So many test casescasecascac
These are actually just 11! -> These are actually just 39916800
No wait! there are 13 -> No waitwaiwaw there are 13
```
---
This is [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'") so shortest submission in bytes, wins! If you liked this challenge, consider upvoting it... And happy golfing!
  
[Answer]
# [05AB1E](https://github.com/Adriandmen/05AB1E/wiki/Commands), ~~23~~ ~~22~~ ~~26~~ ~~25~~ 20 [bytes](https://github.com/Adriandmen/05AB1E/wiki/Codepage)
```
ð¡εD'!åi¨ÐaiηRJë2Lså+!]ðý
```
+4 bytes for two bug-fixes (+1 in case the input is without spaces; +3 for `0!`)  
-5 bytes thanks to *@Grimmy*.
Outputs `2`/`3` for `1!`/`2!` respectively.
[Try it online](https://tio.run/##ATwAw/9vc2FiaWX//8OwwqHOtUQnIcOlacKoRCFzPs63UkpNXcOww73//0kgd2FudGVkIDAhIGdpdmUgbWUgMiE) or [verify all test cases](https://tio.run/##XY89TsNAFISvMt6GxkL5QaIjTRojaABBgVI82w/vwmYXedeJzAE4ADegQKJMg6ho4j6H4CJm11Eayvnez8xYR7niftXOBH5f3yBm7Vz03Wb7vvuaHyXdh9p@zhN3tvu@Or9cdJvupxfHt2l/L@4Y2joPLxlL8oXEFN5ilIhU2JqQW00uikqtwgbjJIoMazKeS0wSHAaTwC/Yo3HIG6VLEJyx6yWZeBF8qA5e5BmZh3J4pmA7Pt2/s0a3kBQ@WcP/0XgApqi5VLnmBNmeG15xDcdsQKb1UpkKWj0xlI@OknzgoCW9xElhS0Zl9QMKSVqzqTi0DkHU0O/ahv6mhecQqyDHA72R7PbJqfBNOGvx2MTcQ6a4m2AqUojRQYvFHw).
**Explanation:**
```
ð¡                  # Split the (implicit) input-string on spaces
                    # (NOTE: `#` cannot be used here if the input doesn't contain spaces)
  ε                 # Map each part to:
   D                #  Duplicate it
    '!åi           '#  If it contains a "!":
        ¨           #   Remove the last character (the "!")
         D          #   Duplicate it
          !         #   Take the factorial of it (strings remain the same)
           s        #   Swap to get the duplicate value again
            >       #   Increase it by 1 (strings remain the same)
             η      #   Get the prefixes of this string/integer
              R     #   Reverse it
               J    #   And join them together to a single string
                M   #   And then push the largest value on the stack
                    #   (which is either the factorial-integer;
                    #    or the integer+1 if it was 0, 1, or 2;
                    #    otherwise it's the longest string, which is the joined prefixes)
  ]                 # Close both the map and if-statement
   ðý               # And join everything with space delimiter again
                    # (after which the result is output implicitly)
```
[Answer]
# [Python 3.8](https://docs.python.org/3.8/), ~~222~~ \$\cdots\$ ~~178~~ 176 bytes
Saved 2 bytes thanks to [Kevin Cruijssen](https://codegolf.stackexchange.com/users/52210/kevin-cruijssen)!!!  
Added ~~2~~ 3 bytes to fix bugs.  
Saved 5 bytes thanks to [ovs](https://codegolf.stackexchange.com/users/64121/ovs)!!!
```
lambda j:(g:=re.match(r"(.*)\b(\w+)!(.*)",j).group)(1)+(str(math.perm(int(f)))*-~(f in'1 2')if(f:=g(2))[0]<':'else''.join(f[:i]for i in range(len(f),0,-1)))+g(3)
import math,re
```
[Try it online!](https://tio.run/##lVPBbtswDL3nKxhfLLVuYCdpmxrzLgMGBNhtBXZIepAd2lbmSIYkt8iC7tcz2mqyDW2B7SCK4iOfRIps967WarZozbHM1sdG7PKNgG3KqjQzONkJV9TMBGxywdc5Wz9d8nGvB9GWTyqju5azhF8y6wwj33rSotkxqRwrOecXVz9ZCVKFCUxDLktWplnFppyv4ocPYRpiYzEMJ1stFStXqXwotQFJAWCEqpA1SHYexdFVQmyXFZvxkdy12jjoL4sMHk12uDcdpl2w7qa3ySyI4LMg2hfD/FPwPHJonYUM2IgF3xAabR24GmFIDmbgNMRjCnwPTAIeUag2AnLdCDv4nk@9oJXr3LtV8pGiEeaD2@k0nXt0CU9COdzAdAxnrHf8DUz/AIaYL@igs5B3stmAAKv0006ogf5tyG@D7AUtq6znohSFoTSFwwikA2mhFZRycnuqwJvw7Pr6ZnE7ny7iu5s4jk@5aNXsoRb0Wq1w7NP4y6aVfu2bvPJMXkq8VIXBjcwbHMPSQwof0YBFVCAUNatUFTTyO9LrBpZzxFk7KS@73wbZC1pLtfwXdl@vWjiCQOzEjx4s9Aah0k0JRS0aatAKqV2oTNK3xX/4S3/DV029pvbQdykUwqInem3uBa1CFD7wvkbrv0sUriPyPWy7/it9fd@DZ3d3yc3C/yEf9RPnIuxnbhiTdARgs5I5TkprhkkOg4N7DuDqIwQHS8rBrGyW4cNzyI@/AA "Python 3.8 (pre-release) – Try It Online")
Replaces `1!` with `11` and `2!` with `22`.
### Ungolfed:
```
import math,re
def f(j):
 m=re.match(r"(.*)\b(\w+)!(.*)" ,j)
 f=m.group(2)
 if f in ('1','2'):f=''
 if re.match(r"\d+",f):
     f=str(math.factorial(int(f)))
 else:
     f=''.join(f[:i] for i in range(len(f),0,-1))
 return m.group(1)+f+m.group(3)
```
[Answer]
# JavaScript (ES6), ~~86 85~~ 80 bytes
Replaces `1!` and `2!` with `0` ... ¯\\_(ツ)\_/¯
```
s=>s.replace(/\w+!/,g=s=>(s=s.slice(0,-1))?1/s?'1006'[s]||s*g(s-1+'#'):s+g(s):s)
```
[Try it online!](https://tio.run/##jVNNj9pADD23v8LZHkjKVwIsC5XonpF6a6Uetj04E5PMdphB8QREtf@ddaCwbNitiGQ78bxnx87LI66RValXvmtdRrvFbMezr9wraWVQUdj/tWkH/U4@k2zIM@6x0ZKOO90kiu6TPt@3kjgetx7499MTf85D7ibt1qdW9IXb8iAh2iln2RnqGZeHi/DGlQipM8jBTRR92F/9PpyytRNLXfqxQcz1mmBJMHpFPGYHoyZ@Dhu0njIYBC@oI1WYlc1ooS1lTeI38lAxpJU2GSCwdZsl2mNXYb4NOIS9r50YW27W/kmAJYFBTx3QHjTDCtlDcndW/z3Q8PZ2PLkbDSbxdBzH8eXAzpotFCizOkvB2ayNI2fd/8hJcM2a5laVlOnUUCD190RLayqBiSyg3fpC2xyM/kMyRF2xfpET6XR3vPkXD2Hvayc2t/MrGlxsukAvIMAl/q1hSvQNuTMLUAUaQzYnkAIs2z2MWy/@eo5u9vvuQESwBU/ypRQy8fkWL09rJ6ZQNSv9KIgPCkDlK@m7hceq1khyppH3QMPpNBlPLsUhmjJOzn0hPwJ6VcAQvIP4tezegiS7Zw "JavaScript (Node.js) – Try It Online")
### How?
Because the callback function of `replace()` is recursive, it's better to feed it with a single variable \$s\$ to keep the recursive calls short. That's why the `!` is captured along with the word preceding it.
The same `slice(0,-1)` is used to remove the `!` on the first iteration and to build the prefixes in case of a string. In order to make the factorial computation compatible with that method, we just have to pad the recursive argument with a random character which is immediately removed by the next iteration.
To deal with the weird edge cases \$1!\$ and \$2!\$, we use the small lookup string `'1006'` to stop the recursion whenever the value is less than or equal to \$3\$. This way, we have \$0!=1\$, \$3!=6\$ and \$n! = 6\cdot\prod\_{k=4}^{n}k\$ for \$n>3\$, but \$1!=2!=0\$ (sic).
### Commented
*NB: alternate slash symbols used in the regular expression to prevent the syntax highlighting from being broken*
```
s =>                        // s = input string
  s.replace(                // find in s
    ⁄\w+!⁄,                 // a word followed by a '!'
    g = s =>                // g is a recursive function computing the replacement
      (s = s.slice(0, -1))  //   remove the last character from s
                            //   (for the 1st iteration, it removes the '!')
      ?                     //   if the resulting string is not empty:
        1 / s ?             //     if this is a numeric value:
          '1006'[s]         //       0 -> 1, 1 -> 0, 2 -> 0, 3 -> 6
          || s *            //       for all other values, multiply s by
             g(s - 1 + '#') //       the result of a recursive call with s - 1,
                            //       padded with a '#' for the next slice()
        :                   //     else (s is a string):
          s +               //       append s
          g(s)              //       append the result of a recursive call
      :                     //   else (s is empty):
        s                   //     stop recursion
  )                         // end of replace()
```
[Answer]
# [Python 3](https://docs.python.org/3/), ~~157~~ 155 bytes
Thanks to [Noodle9](https://codegolf.stackexchange.com/users/9481/noodle9) for -2 bytes.
```
(*s,w),b=map(str.split,input().split('!'))
w*=1+(w in'1 2')
if'A'>w:
 i=int(w);w=1
 while i:w*=i;i-=1
else:
 d=w[:-1]
 while d:w+=d;d=d[:-1]
print(*s,w,*b)
```
[Try it online!](https://tio.run/##VZFBa9wwEIXv@hXPe7G9cUq9KRR2caGHFAKFHhrIofQwtidrNVrJSHJU989vRzU59CTm0@jNe5p5jZOzd9fBjYwOu93uWu1Dk@qm7y40VyH6d2E2Ojbazkus6q2qyqKsa5X2XXtTJWhbtjiUtdLP5efyUzoq6E7bWKX6lLpWIU3aMPRRHuiTvhXEJrC0jV36cbxtf761jMd0042nsRs3PPsskx01@76@ij@lts5Hv2QBRL/mA@DfPFQ5R61yMfAccf/ty733zm8dvWd6uT4xjAsRcWJcKA4T7hAd3hfKeULvDIVCnfWr3DI@FOoBiWzkEYcCb/igvnLEEtAv2owgBOvShWyhRJ28TKDIDXSEDphJprUfs5KzZsVEIuIs/w9aKe3gedS94QIPG7X8yh6B2YKs7ErbM4x@kb@M6mmiKBR0oT@Z/9vh2ZlnDBMZw/bMElIMaAn03UlYuyKymBkosLDHicNml4a4yJMVv5bstS3@Ag "Python 3 – Try It Online")
[Answer]
# [Wolfram Language (Mathematica)](https://www.wolfram.com/wolframscript/), 173 bytes
```
StringRiffle[""<>If[Last@#=="!",If[NumberQ[n=ToExpression[""<>#]],If[n>0&&n<3&&First@#!="0","",ToString@n],Rest@NestList[Most,#,Length@#-1]],#]&/@Characters/@StringSplit@#]&
```
[Try it online!](https://tio.run/##bY09a8MwEIb3/gr7BJpU4jRrFASlhYIb2iSb0KCEsyywpSBdS/@9qjRDFy8H73Pvx2xpxNmSv9jiZDlS8sEd/DBMqAG2u7dB9zaTYlJCC6LK/dd8xvSpgzzFl59rwpx9DH9mZszNEXYd52G74fzVp1u2ldCBABCneB9QwYgD1te@nt5n0u8xk2Cix@BoVOxxXauY4Sv1PNpkL4Qpr9Q9fLxOvpYaXj6qpEY5DTHZ5hwnm1swD//Y@W9sZmy6Zbxexk/LeFNxKb8 "Wolfram Language (Mathematica) – Try It Online")
*-2 bytes from @Kevin Cruijssen*
[Answer]
# [Perl 5](https://www.perl.org/) `-p -MList::Util=product`, 93 bytes
```
s/(\d+)!/$1?$1>2?product 1..$1:'':1/e;s%(\w+)!%join'',map{substr$1,0,$_}reverse 1..length$1%e
```
[Try it online!](https://tio.run/##dY8xT8MwEIX3/IqL5ChFJE0dyhJEOyPBiFgqIbs@EiPXtuxLOyD@OiapgE4M93Tv3veG8xjMbUqxWezU9VXeML5lfNNufXBq3BPw5ZLxriw73uBdLBa700QV707bsqwOwn/EUUYKjFerir1@BjxiiDjXDNqeBsYLTOkFwbhIQAPCQdB@gBsgB6sc6g38E/LMBQHSGRHP2J@bZRrpZNbr49RBWJ@JX9euswc4CUuooM0v55kZrcI3bVF9OU/a2ZjqJ6l7bWlaHnWkrnsmbe5//k@1N98 "Perl 5 – Try It Online")
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), ~~22~~ 21 bytes
```
;”ḟV$¹ƤṖṚƊfØD$?ċ¡€”!K
```
[Try it online!](https://tio.run/##VY8xSwNBEIX7@xVvIa1gomAR0MYmaKcYLOfuxruNm1253Us4K7EJaC8iigiWVmKRYJdI/sflj8Q5goXlfPPmzXsDNqZar7urm5d6@nrWms@W7/XsoZ49Le8uFo@HrYOf@/nb6vZDBOqo0T1jax8ydevp52LSiurvr8VEBOfrdZ9hnA8IOWNIIcmxg@CwrSJXEGJnyKso0yPZMnZV1MOYbOAUHYU/3ImOOaD0iEttUhC8deMhWRWJOxXygQIj5oRKz9AB2uOK5Gl7rzF01lTISbyc5f@gLaNNCk51bFiht6GWR1zAM1uQrUKubQajLxvrqJ9TEAoa0nXDE5cyMmcukORkDNuMpasE0NLrxElnWyGwhEnIs7DTnP0mNSWhlJMKg7LJ2la/ "Jelly – Try It Online")
A full program taking a list of words and printing a string. For the 1 and 2 cases the 1 and 2 are dropped from the output. Saved a byte now that a list of words is permitted input. 
## Explanation
```
Ḳ                      | Split at spaces
                ċ¡€”!  | For each word, if it contains a ! then do the following:
           fØD$?       | - If any characters are left after restricting to digits:
     $                 | - Then:
 ;”ḟ                   |   - Append ḟ (which will filter out 1 and 2 at the next step because x! == x)
    V                  |   - Evaluate (i.e. calculate the factorial and then filter out the input number) 
          Ɗ            | - Else:
      ¹Ƥ               |   - Prefixes
        Ṗ              |   - Remove last
         Ṛ             |   - Reverse
                     K | Finally, join with spaces
```
[Answer]
# [Retina](https://github.com/m-ender/retina/wiki/The-Language), 70 bytes
```
' %/!$/&/^[012]!$/(`..
$.(*__
/\D./(^`.
$`
.+
*
_
$.>`$*
~`^
^.$*¶$$.(
```
[Try it online!](https://tio.run/##jVFLbttADN3PKWhAaQy31cdxHHuTVYFCQHcNkEVaW5TEWNOOOYVmlMBZ5Fg9QC/mcKTGRZIGyIKPnMdHiiJb8pox2x@NPxf7YzhKRlHyLlldpdn0u4TjIo5VFI8n67VKvn2Kk/GqEKJQ8Xs1UWtJnRfRRN0XK7WKo8mf35GI93vbIpTWoBvBx3M4vAKIlbZUG31DsCWY9YrH13SmcrhF9lTDdPSPDpqOa7rWTLX6Qh46B2WnTQ0Iju3tFrlv9P/U4HoMIObYqUsCbAkMevoA2oN28Audh@ysb/Va@uT0dL44m00X6XKepqkMbNnsoEGZ1TL1tc84y/aJLBs9/aGcq5ZqXRqpzgcJ0w214IgYkHe@0bwBo39SmCR84FBxiB6Dv35wPQYQyzl/Q3d12aAXFnCLd4GvbE2wseYaqgaNId4QSIWThQx7erteq68W5B478CSbrNDR0OMlHUCswkpdNOSGW2DlO2m5gx9duNOwxtfSJ8tlNl@k6QM "Retina – Try It Online") Link includes test cases. Explanation:
```
' %
```
Split the input into words.
```
/!$/&
```
Only process words that end in `!`.
```
/^[012]!$/(`
```
If this is `0!`, `1!` or `2!`...
```
..
$.(*__
```
... then return the incremented digit. (Approach shamelessly stolen from @Arnauld.)
```
/\D./(
```
If the word contains a non-digit (excluding the trailing `!`)...
```
^`.
$`
```
... then replace it with all of its prefixes in reverse order. (Note that these are exclusive prefixes so that the empty prefix is included but the string including the `!` is excluded.)
```
.+
*
_
$.>`$*
~`^
^.$*$n$$.(
```
Otherwise compute the factorial.
[Answer]
# [SNOBOL4 (CSNOBOL4)](http://www.snobol4.org/csnobol4/), 206 bytes
```
	D =84 ** 9
	INPUT ARB . L SPAN(&UCASE &LCASE D) . W '!' REM . R
	W SPAN(D) 	:F(S)
	N =W + 1	LT(W,3)	:S(O)
	N =W
F	W =W - 1	GT(W,1)	:F(O)
	N =N * W	:(F)
S	N =N W
	W ARB . W RPOS(1)	:S(S)
O	OUTPUT =L N R
END
```
[Try it online!](https://tio.run/##LY65DoMwEETr9VcMDdjkkBAUCRIFZxSJ2MgGuU8dhSL/L7Ic1R5vZ2d@3/k9f7JloQbFLUMc4y7oqYdpRGkrXNHDDaWW4VSXrkXYb6VRTDyiIIJtX9xbQX4/ZER5J50SpFF4nJBQP0p/ThXlTppjLzoWML4wfqw4UavswBoxPOWyU8Lts18N9kQedjBOJts/9jFkpnENXPTQnKTVzbKUFdIAdfMH "SNOBOL4 (CSNOBOL4) – Try It Online")
`84 ** 9 = 208215748530929664` which has all the digits from 0-9.
```
	D =84 ** 9						;* D contains every decimal digit.
	INPUT ARB . L SPAN(&UCASE &LCASE D) . W '!' REM . R
	;* split the input into an ARBitrary Left part, the Word followed by a '!' and the REMainder to the Right part.
	W SPAN(D) 	:F(S)					;* if W has digits, goto S
	N =W + 1	LT(W,3)	:S(O)				;* if W < 3, then N = W + 1 and goto O
	N =W							;* set N = W
F	W =W - 1	GT(W,1)	:F(O)				;* decrement W, and goto O if W == 0
	N =N * W	:(F)					;* N = N * W, goto F
S	N =N W							;* W is a string, so set N = N W
	W ARB . W RPOS(1)	:S(S)				;* set W to W excluding its final character and if W had any characters, goto S
O	OUTPUT =L N R						;* output Left, the New middle, and the Right string.
END
```
[Answer]
# [Red](http://www.red-lang.org), ~~242~~ ~~225~~ 204 bytes
```
func[s][c: charset[#"0"-#"z"]parse s[any[change[copy t any c"!"](
case[find"12"t[0]t <"A"[t: to 1 t p: 1 while[t > 1][p: p * t
t: t - 1]p]t >":"[p: copy t until[take/last p append t p p =""]t]])| skip]]s]
```
[Try it online!](https://tio.run/##RVBNT8MwDL33V7x5F0CaWAenCiZxROIESByiHLLWXcu6JGpcpiH@e3G1DRJFkd6H/eyeq/GVK2OzuhjrwZcmWVMWKBvXJxYzpyUt5vRNNk4AknH@aJT1WzZliEcIFEFJM7JXWekSm7r1FeUrErO0ggd6IiMFJCBXcSz0OzRtx0awRm6NIhE3kGwSYaFQVNuaCpqoc4/BS9sZcTu@7VzSMnAxsq@minofiaxYe/2DtGujtcmOdejZlQ0STAY99MHoglqlYeydKHU3hVrO6MSH3mETtPoF2LZfqmTcX4BnHJwXrrCa4UKuztwLa8iEzdB2FRySD4e98//O4LsjGqem4PkCvwVNotsT1lzT7v56vzes23a9vlIG16n5c1BRnqvCniaKfevF6Jx7HZIWa0KNZDM7/gI "Red – Try It Online")
[Answer]
# [R](https://www.r-project.org/), ~~205~~ ~~201~~ ~~194~~ 189 bytes
```
s=scan(,'');l=grepl('\\w!',s);w=s[l];n=nchar;k=function(m,x)substr(x,1,n(x)-m);w=k(1,w);b=as.numeric(w);o=`if`(is.na(b),Reduce(paste0,sapply(0:n(w)-1,k,w)),gamma(b+1));s[l]=o;if(o!=w)cat(s)
```
[Try it online!](https://tio.run/##FY5LbsMwDESvYq9MokwRbyvoBl112xQIrcipYH0MUYLc07vKcgbvDSafp2gxHIGmCZXXz2x3D9Pt1saJBFXT8u1/VNTR/HJWm15rNMWlCIEOlLpIyXDQTBEOvISXsMFMDdWiWd5jDTY7Az0nfXfrHVwvGRakL/uoxsLOUuyVhPfd/8H1I3b2MtPWJ5CeHEKH32ZE9fqhk3IrpFE3NFxA8Py0ZagyLNX5x8CDxNQCx/H8Bw "R – Try It Online")
[Answer]
# [Ruby](https://www.ruby-lang.org/), ~~88 85~~ 83 bytes
```
->s{s.sub(/\S+!/){|z|z<?1?1:z>?9?z.chars.map{z=z.chop}*'':eval([6,*4..z.to_i]*?*)}}
```
[Try it online!](https://tio.run/##ZcjNDoIgAADge0@BXlQqjHJtuZSH6GiuQcNy08Eg3QJ5dvq5cvw@NbG37yq/rbXVSE8sza@XdZRndjGLORNMcGlqciIG3Z9UaTRSaU31k5AOJknJZzqkzXEDC4QMeolb30ICM@e8BF0TC0UBEwPVUdyu/vPoZw5GDopgcDD7YA7B7L7jPw "Ruby – Try It Online")
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), 26 [bytes](https://github.com/DennisMitchell/jelly/wiki/Code-page)
```
ḲṖVN!Ƒ¡!ƲṖƤṚ$<”AẠ$?¹ċ?€”!K
```
A full program accepting a string which prints the result.
**[Try it online!](https://tio.run/##y0rNyan8///hjk0Pd04L81M8NvHQQsVjIM6xJQ93zlKxedQw1/HhrgUq9od2Hum2f9S0Biig6P3//39PhfLEvJLUFAUjRYX0zLJUhdxUBSMA "Jelly – Try It Online")**
Kind of a tough one for Jelly.
### How?
```
ḲṖVN!Ƒ¡!ƲṖƤṚ$<”AẠ$?¹ċ?€”!K - Link: list of characters, s
Ḳ                          - split at space characters -> words
                       ”!  - literal '!' character
                      €    - for each word:
                     ?     -   if...
                    ċ      -   ...condition: count (i.e. contains?)
                  ?        -   ...then: if...
                 $         -            ...condition: last two links as a monad:
             <             -              less than:
              ”A           -                literal 'A' character
                Ạ          -              all?
        Ʋ                  -            ...then: last four links as a monad:
 Ṗ                         -              pop (remove the '!')
  V                        -              evaluate as Jelly code (get an integer)
      ¡                    -              repeat...
     Ƒ                     -              ...number of times: is invariant under?:
    !                      -                factorial
   N                       -              ...action: negate (i.e. 1;2;X -> -1;-2;X)
       !                   -              factorial (-1! = -2! = inf)
            $              -            ...else: last two links as a monad:
          Ƥ                -              for prefixes:
         Ṗ                 -                pop (remove the `!` from each prefix)
           Ṛ               -              reverse
                   ¹       -   ...else: no-op
                         K - join with spaces
                           - implicit, smashing print
```
[Answer]
# [Husk](https://github.com/barbuz/Husk), 23 bytes
```
wm??öΣ↔ḣhȯs§Y→ΠiV√I€'!w
```
[Try it online!](https://tio.run/##VY8xTsNAEEV7n@JvRUsCEmVqS3QgIsqxPXiXrHeRdx3LdKSIqBEVFVIKGiS6iDo0VLkDvogZK6KgnDeaN//rJiyGql897LZDW81mX9v9pl8//3xu9PdH2L1d9@un/au56h9f0n71fqTaYRjmDOtDRNSMimKucYLocawSXxMybymopDRL2TJOVZKiJRe5wFThD0@Tc45oArLG2AKE4HxbkVOJ2KmWDxQZGefUBIaJMAF3JE8nZ6PQO9tBk7i84/9gIqPLay5MZlkhPVDHS64RmB3IdVEbV8KaxahO5pqiUFBF9yPPfcEovb1BrsladiVLVwlgpNeFl86uQ2QJk1NgYZeawyE15bGRkw63zZh1on4B "Husk – Try It Online")
Maps 1 to 2 and 2 to 3.
On my phone, so writing an explanation is not too easy, but I'll try.
Code with parentheses for "clarity":
```
wm(?(?(Σ↔ḣh)(s§Y→Πi)(V√))(I)(€'!))w
```
The main part of the code is built by conditionals, they may be a bit easier to read once you know that in Husk `?abc` means `if c then a else b`. Also, most of the time `fg` is the composition of functions `f` and `g`, so we are actually applying `g` before `f` (to oversimplify it, you should read the code backwards)
A pseudocode for this could be:
```
Split input into words, map the following function to each word and then join them again.
  If word contains '!':
    if any character of the word is a letter:
      get the head of the word (drop final '!')
      get the list of prefixes (heads)
      reverse it
      join prefixes in a single word
    else:
      convert word to integer
      get the maximum between:
        successor
        factorial
      convert it back to string
  else apply Identity function (do nothing)
```
[Answer]
# [Charcoal](https://github.com/somebody1234/Charcoal), 57 bytes
```
≔⌕θ!η≔⊟⌕Ａ⁺ …θη ζ…θζ≔✂θζη¹ε≡ε2¹1¹0¦1¿ΣεＩΠ…·¹Ｉε⭆ε…ε⁻Ｌεκ✂θ⊕η
```
[Try it online!](https://tio.run/##bVBLasMwEN37FBOtxuBC3GWyCoZCIQETn0CVJ5aoLCeWlNCUnt0df0hbqDaD3m94o7TsVSftMOy8N43DF@NqvGQgViLNQKfbZCHK7jyRO2uxtNGjAJFB8aEsFZq5y6hmC8PjuLOz7I0L@EdyT38SK2sUTSBbM8jZRcz6mwlKA1IKn4mSnkA8iw3MYTkLZiz/B1s/MKYZrekkow0bMCfAKracmS6CQvqAZd/VUQV8dYobmSsdpWsIc@410qzmtwWyHD7bqsCjOcgz0u/y/DkYx0fZk2uCZmcG75M5@VrO8GjLy3pqyQWqUY@SYdhTgOjhLRpbgwTvulsr3SoZnq72Gw "Charcoal – Try It Online") Link is to verbose version of code. Explanation:
```
≔⌕θ!η
```
Find the position of the `!`.
```
≔⊟⌕Ａ⁺ …θη ζ
```
Find the position of the start of the word ending in `!`.
```
…θζ
```
Print the prefix of the input before the word.
```
≔✂θζη¹ε≡ε
```
Extract the word and switch on it.
```
2¹1¹0¦1
```
If it's `2` or `1` then output a `-`, otherwise if it's a `0` then outputs `1`, otherwise...
```
¿ΣεＩΠ…·¹Ｉε
```
... if it's an integer then output its factorial, otherwise...
```
⭆ε…ε⁻Ｌεκ
```
... output its prefixes in reverse order. (These are inclusive prefixes, so they include the word itself but not the empty string.)
```
✂θ⊕η
```
Print the suffix of the input after the `!`.
[Answer]
# [Japt](https://github.com/ETHproductions/japt), 27 [bytes](https://en.wikipedia.org/wiki/ISO/IEC_8859-1)
Well, this is just god-awful but I've spent far too long on it to *not* post it. Kids, let this be a warning against trying to golf without your full faculties about you!
```
r"(%w+)!"Ï<3?°Y:Yn ÊªYå+ Ôq
```
[Try it](https://petershaggynoble.github.io/Japt-Interpreter/?v=1.4.6&code=ciIoJXcrKSEizzwzP7BZOlluIMqqWeUrINRx&input=WyJhYmNkISIsIldlIGxvc3QgdGhlIG1hdGNoIDMgdG8gMCEiLCJvcmEgYm9sYXMhIiwiZ2l2ZSBtZSA0ISIsIkkgd2FudGVkIDIhIGdpdmUgbWUgMiIsIkxldCB1cyBidWlsZCBhIHNub3dtYW4hIiwiV2UgYXJlIGxhdGUgSXQgaXMgcGFzdCAxNyEiLCJJIG9ubHkgaGF2ZSBvbmUhIiwiSSBvbmx5IGhhdmUgMSEiLCJJbmNyZWRpYmxlISBJIGhhdmUgbmV2ZXIgc2VlbiBhbnl0aGluZyBsaWtlIGl0IiwiV2hhdCBhbiBhbWF6aW5nIGNvZGUgZ29sZiBjaGFsbGVuZ2UgdGhpcyBpcyEiLCJTbyBtYW55IHRlc3QgY2FzZXMhIiwiVGhlc2UgYXJlIGFjdHVhbGx5IGp1c3QgMTEhIiwidGVzdCEgMyIsICIwISIsInRlc3QiXQotbVI)
[Answer]
# [05AB1E](https://github.com/Adriandmen/05AB1E), ~~26~~ ~~22~~ 26 bytes
```
ð¡εD'!¢i¨D.ïiÐ!Ês!*ëηRJ]ðý
```
[Try it online!](https://tio.run/##yy9OTMpM/f//8IZDC89tdVFXPLQo89AKF73D6zMPT1A83FWsqHV49bntQV6xhzcc3vv/f0lqcYmiAogEAA "05AB1E – Try It Online")
[Answer]
# [C# (Visual C# Interactive Compiler)](http://www.mono-project.com/docs/about-mono/releases/5.0.0/#csc), 227 bytes
```
s=>{var w=s.Split("!")[0].Split(" ").Last();if(!int.TryParse(w,out int n))for(;(w=w.Substring(1)).Length!=0;)s=s.Replace("!",w+"!");else{for(int i=n;--i>0;n=n*i);s=s.Replace(w+"!",""+(n<1?1:n>4?n:0));}return s.Replace("!","");}
```
[Try it online!](https://tio.run/##bVLBbtswDL3nK2ifpDUJ4rbAgLpObwMKBMOwFOih6EFRmFibQgUSHSML8u0ZXcfDBuwkkI@Pj3yUTROb3OVLQ/YxcXS0HUP/zmFTXVI1Px1MhLZK0@XeO1Z5luu32fsQQa6nC5NY6dJtVOaIpy/x@M3EhKodh4ZBUkBab0JUpWqrdrpsVr2CKrSQkbZcZ9Ws1ElEvuPeG4udzLi96cRK9AlPHb3r5CoqJxM3n5VU0Seny79JH4Rxnt8oeiyeigea3z/Rw0zr8hyRm0jwr0Au3c@XctRtyFABYfv2fspfEXxIDFwj7AzbGu6AA8yEMQLIQzSwCt6ka7x1B6lDuL/Gz9AaYlzDbQYDdttDC2RoEqwa59dgIFFod4auPJE1UaQNI6zQmiYhONk4wV4MhuLzn/6B/BFqI60D4X@yxZAjG3HtVh4zeO4hwgNGSIgEho5cyxnAu5@d0nWK2rBAYHbmVwfasEbYBr8BWxvv5VooxshQbth/GcQlOgKjTGlNwgF4qTH1OxnLjZCP8KPpNhnm@xrEKsdZ53TsK4u7/FyO5NpobK26w8gf2n98I2AtpNfoGBeOUG1UD8l5L78B "C# (Visual C# Interactive Compiler) – Try It Online")
[Answer]
# C (GCC), 236 bytes
```
i,j;char*a,*b,*c,*d;f(char*s){a=strchr(s,33);for(b=a;b>s&!isspace(b[-1]);--b);for(d=s;d-b;)putchar(*d++);*a=0;if(*b&64)for(c=a;*b;*--c=0)printf("%s",b);else{for(i=j=1;j-atoi(b);)i*=++j;printf("%d",i);}printf("%s",a+1);j>0&j<3&&puts(b);}
```
[Try It Online!](https://tio.run/##Tc5NTsMwEAXgdXMKJxKR7dhVnAKbwV1zAHalC/8k1CEkVSbAourZg0Ohqhe2NP7e0zj55tw8B9GCO5iRG8Gt4E5wDw39nSA7GY3T6A4jRbHZMGiGkVptwG4xTwPi0bia2p1UewZS2gvwGsFLC@z4OS09lPuiYMCNLiE0lNv88Z4t0MUmboFL6XTJjmPop4Zmd5iJ2FR3WJ8WFXSrFbTSTEOg8YMFrouihav3mQgMzrd5UygG7bbM26dNnsc9cEme5yjIhwk9/RqCZ8kpWS0LEtztiSbZc911Q0peapzIer1@7TNIVg1FFp9/qS70IY23qsqrUbeouiCVkoDkPfSeDA35rsPol96YIH8nBqsYPM8/)
Explanation:
```
i,j;
char*a,*b,*c,*d;
f(char*s)
{
    // a is the first '!' in s
    a=strchr(s,33);
    // b is the first character after the first whitespace before a, or s if there is no whitespace there
    for(b=a;b>s&!isspace(b[-1]);--b);
    // Print each character from s to b
    for(d=s;d-b;)putchar(*d++);
    // Null-terminate s at a
    *a=0;
    // If b is alphabetic
    if(*b&64)
        // While there's anything left of b, print b and shorten it by adding an earlier null terminator
        for(c=a;*b;*--c=0)
            printf("%s",b);
    else {
        // If b is not alphabetic, it's assumed to be a number.
        // i is the product of all j-s between 1, and b as a number
        for(i=j=1;j-atoi(b);)i*=++j;
        printf("%d",i);
    }
    // Print everything after the exclamation mark
    printf("%s",a+1);
    // Print the number twice if it's 1 or 2
    j>0&j<3&&puts(b);
}
```
[ceilingcat](https://codegolf.stackexchange.com/users/52904/ceilingcat)'s 226 byte version:
```
i,j;char*a,*b,*c,*d;f(char*s){for(b=a=index(s,33);b>s&!isspace(b[-1]);--b);for(d=s;*a=d-b;)putchar(*d++);if(*b&64)for(c=a;*b;*--c=0)printf(b);else{for(i=j=1;j-atoi(b);)i*=++j;printf("%d",i);}printf("%s",a+1);j>0&j<3&&puts(b);}
```
[Try It Online](https://tio.run/##TZDLTsMwEEXX7Vc4kYg8jl3VLbAZ3DUfwK504VfAoSRVHB5S1W8PdgsVXszInnPvHdmKF2unKfAW7asemObMcGY5c9jQ80uEY9MP1CitQuf8N418vQY0m1gVIcaDtp6arZA7QCEMYIadisi0csIgHD7G7EOZq2vA0FBmqvtbyJhVGplBJoRVSzgMoRsbmiz8PvpzaFCtktgKPfYhDyAwVdct/qLljSt5ADxd77HkupaA7WZZtQ/rqkrpMStPUyLIuw4d/eyDg/lxPstrkbjdEUXKR7/f9wV58nEki8XiuStxPmtohNT@SHlB74pU5Wp5ZeR/aHWBZEFCJG/px0jfkC8fBpd9k4L8niRcJeFp@gE)
]