SNOBOL4, 437 430 bytes
N = TRIM(INPUT)
D = REPLACE(N,'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+'abcdefghijklmnopqrstuvwxyz')
B = "b" D
F = "f" D
M = "m" D
&ANCHOR = 1
D SPAN('bcdfghjklmnpqrstvwxyz') . I REM . R :F(Y)
B = "b" R
F = "f" R
M = "m" R
I "b" :S(U)
I "f" :S(V)
I "m" :S(W) F(Y)
U D "b" REM . B :(Y)
V D "f" REM . F :(Y)
W D "m" REM . M
Y OUTPUT = N ", " N ", bo-" B
OUTPUT = "Banana-fana fo-" F
OUTPUT = "Fee-fi-mo-" M
OUTPUT = N "!"
END
Ungolfed (plus I added a prompt; the one above just waits for a name to be typed):
OUTPUT = "Please enter your name."
Name = TRIM(INPUT)
UC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
LC = 'abcdefghijklmnopqrstuvwxyz'
Low = REPLACE(Name, UC, LC)
BName = "b" Low
FName = "f" Low
MName = "m" Low
Consonants = SPAN('bcdfghjklmnpqrstvwxyz')
&ANCHOR = 1
Low Consonants . First REM . Rest :F(READY)
BName = "b" Rest
FName = "f" Rest
MName = "m" Rest
First "b" :S(BINIT)
First "f" :S(FINIT)
First "m" :S(MINIT) F(READY)
BINIT Low "b" REM . BName :(READY)
FINIT Low "f" REM . FName :(READY)
MINIT Low "m" REM . MName
READY OUTPUT = Name ", " Name ", bo-" BName
OUTPUT = "Banana-fana fo-" FName
OUTPUT = "Fee-fi-mo-" MName
OUTPUT = Name "!"
END
This is the first SNOBOL program I've ever written.
SNOBOL is a line-oriented language, like FORTRAN, COBOL, or BASIC. Each line consists of an optional label starting in column 1, the code for the line which can involve assignments and pattern matching, and an optional branch. Yes, lines end with (optional) GOTOs. They come in two forms:
:(TARGET)
Branches to label TARGET
, while
:S(SUCCESS) F(FAILURE)
Branches to SUCCESS
if the pattern match succeeded, or FAILURE
otherwise. You can also just branch on success and fall through to the next line on failure, or vice versa.
Continuation lines begin with a +
or .
. Comments begin with a *
.
How does it work?
Read in a name, convert it to lower case. Set up the B-, F-, and M-names assuming it begins with a vowel. Then check if it begins with a span of consonants. If not, we're ready to go! If so, strip the leading consonants and set up the B-, F-, and M-names assuming it doesn't begin with any of those letters. Finally, check if it starts with each of those letters in turn, fixing up the names as needed. Then we're ready to play the name game!
Sample run:
# $RUN *SNOBOL4 5=GOLF.SNO+*SOURCE* 6=*DUMMY*(1,28)+*SINK*(1,4)+*DUMMY*
# Execution begins 16:57:25
Snowman
Snowman, Snowman, bo-bowman
Banana-fana fo-fowman
Fee-fi-mo-mowman
Snowman!
# Execution terminated 16:57:30 T=0.013
I ran this on the Hercules S/370 mainframe emulator running the 6.0a release of the Michigan Terminal System using SNOBOL4 version 3.10 from April 1, 1973 built for MTS on May 1, 1975, but there are probably easier ways to run SNOBOL4 on a modern system. :)
Edit: Removed a redundant success branch that was equivalent to a fallthrough (I didn't realize I could put just a failure branch by itself) which eliminates an unneeded branch label, and turned an unconditional goto into a failure branch on the prior line, for a savings of 7 bytes.
Now that TIO has SNOBOL4 support you can
Try it online! Note: It shows the size as 429 rather than 430 because when I pasted it there, the final linefeed got removed. I tried changing the continuation line (the one beginning with +
) to a single line, which wasn't legal on the mainframe version because the line was too long, and it worked and brought it down to 427. Obviously CSNOBOL4 allows longer lines. I'm going to leave my score at 430, though, because that's how many bytes the script was on my machine, and besides, SNOBOL is pretty non-competitive.
Y
is handled as a vocal, soYves
is likeIves
orArnold
. \$\endgroup\$Y
as a vowel only if it's followed by a consonant. That should cover those cases at least. \$\endgroup\$