Another update from ingydotnet^djgoku

This commit is contained in:
Ingy döt Net 2015-11-18 06:14:39 +00:00
parent 91df62d461
commit 948b86eafa
7604 changed files with 108452 additions and 22726 deletions

View file

@ -0,0 +1,34 @@
# syntax: GAWK -f MAD_LIBS.AWK
BEGIN {
print("enter story:")
}
{ story_arr[++nr] = $0
if ($0 ~ /^ *$/) {
exit
}
while ($0 ~ /[<>]/) {
L = index($0,"<")
R = index($0,">")
changes_arr[substr($0,L,R-L+1)] = ""
sub(/</,"",$0)
sub(/>/,"",$0)
}
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
print("enter values for:")
for (i in changes_arr) { # prompt for replacement values
printf("%s ",i)
getline rec
sub(/ +$/,"",rec)
changes_arr[i] = rec
}
printf("\nrevised story:\n")
for (i=1; i<=nr; i++) { # print the story
for (j in changes_arr) {
gsub(j,changes_arr[j],story_arr[i])
}
printf("%s\n",story_arr[i])
}
exit(0)
}

View file

@ -0,0 +1,70 @@
::Save this as MADLIBS.BAT
@echo off
setlocal enabledelayedexpansion
%== Check if there is no arguments ==%
if "%~1"=="" (
echo.
echo.[Mad Libs - Batch File Implementation]
echo.
echo.Usage: MADLIBS [file]
echo.
exit /b 1
)
if not exist "%~f1" (echo.File not found.&exit /b 1)
echo.
%== Read the text file ==%
echo.[Mad Libs - Batch File Implementation]
echo.
for /f "tokens=* eol=_" %%A in (%~sf1) do (
set /a cnt+=1
set "line!cnt!=%%A"
)
%== User input the missing parts ==%
for /l %%. in (1,1,!cnt!) do (
call :proc_line "%%."
)
%== Display the edited story... ==%
echo.
echo.The Story:
echo.
for /l %%? in (1,1,!cnt!) do (
echo. !line%%?!
)
echo.
exit /b 0
%== The main processor of the story ==%
:proc_line
set "str=!line%~1!"
:loop
if "!str!"=="" goto :EOF
for /f "tokens=1,* delims=<" %%M in ("!str!") do (
for /f "tokens=1,* delims=>" %%X in ("%%M") do (
if not "%%M"=="%%X" (
set "temp_var=%%X"
if not "!temp_var: =!"=="" (
set "input="
set /p "input=Enter a value for [%%X]?"
call :subst_input
)
)
)
)
set "str=!line%~1!"
for /f "tokens=1,* delims=<" %%M in ("!str!") do (set str=%%N)
goto loop
%== This Substitutes the input to the blank ==%
:subst_input
set "chk_brack=!input:>=!"
set "chk_brack=!chk_brack:<=!"
set "chk_brack=!chk_brack:%%=!"
for /l %%. in (1,1,!cnt!) do (
if "!line%%.: =!"==" =" set "line%%.= "
if "!chk_brack!"=="!input!" (
call set "line%%.=%%line%%.:<%%X>=!input!%%"
) else (set "line%%.=!line%%.:<%%X>=!")
)
goto :EOF

View file

@ -0,0 +1,145 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define err(...) fprintf(stderr, ## __VA_ARGS__), exit(1)
/* We create a dynamic string with a few functions which make modifying
* the string and growing a bit easier */
typedef struct {
char *data;
size_t alloc;
size_t length;
} dstr;
inline int dstr_space(dstr *s, size_t grow_amount)
{
return s->length + grow_amount < s->alloc;
}
int dstr_grow(dstr *s)
{
s->alloc *= 2;
char *attempt = realloc(s->data, s->alloc);
if (!attempt) return 0;
else s->data = attempt;
return 1;
}
dstr* dstr_init(const size_t to_allocate)
{
dstr *s = malloc(sizeof(dstr));
if (!s) goto failure;
s->length = 0;
s->alloc = to_allocate;
s->data = malloc(s->alloc);
if (!s->data) goto failure;
return s;
failure:
if (s->data) free(s->data);
if (s) free(s);
return NULL;
}
void dstr_delete(dstr *s)
{
if (s->data) free(s->data);
if (s) free(s);
}
dstr* readinput(FILE *fd)
{
static const size_t buffer_size = 4096;
char buffer[buffer_size];
dstr *s = dstr_init(buffer_size);
if (!s) goto failure;
while (fgets(buffer, buffer_size, fd)) {
while (!dstr_space(s, buffer_size))
if (!dstr_grow(s)) goto failure;
strncpy(s->data + s->length, buffer, buffer_size);
s->length += strlen(buffer);
}
return s;
failure:
dstr_delete(s);
return NULL;
}
void dstr_replace_all(dstr *story, const char *replace, const char *insert)
{
const size_t replace_l = strlen(replace);
const size_t insert_l = strlen(insert);
char *start = story->data;
while ((start = strstr(start, replace))) {
if (!dstr_space(story, insert_l - replace_l))
if (!dstr_grow(story)) err("Failed to allocate memory");
if (insert_l != replace_l) {
memmove(start + insert_l, start + replace_l, story->length -
(start + replace_l - story->data));
/* Remember to null terminate the data so we can utilize it
* as we normally would */
story->length += insert_l - replace_l;
story->data[story->length] = 0;
}
memmove(start, insert, insert_l);
}
}
void madlibs(dstr *story)
{
static const size_t buffer_size = 128;
char insert[buffer_size];
char replace[buffer_size];
char *start,
*end = story->data;
while (start = strchr(end, '<')) {
if (!(end = strchr(start, '>'))) err("Malformed brackets in input");
/* One extra for current char and another for nul byte */
strncpy(replace, start, end - start + 1);
replace[end - start + 1] = '\0';
printf("Enter value for field %s: ", replace);
fgets(insert, buffer_size, stdin);
const size_t il = strlen(insert) - 1;
if (insert[il] == '\n')
insert[il] = '\0';
dstr_replace_all(story, replace, insert);
}
printf("\n");
}
int main(int argc, char *argv[])
{
if (argc < 2) return 0;
FILE *fd = fopen(argv[1], "r");
if (!fd) err("Could not open file: '%s\n", argv[1]);
dstr *story = readinput(fd); fclose(fd);
if (!story) err("Failed to allocate memory");
madlibs(story);
printf("%s\n", story->data);
dstr_delete(story);
return 0;
}

View file

@ -0,0 +1,48 @@
#include <iostream>
#include <string>
using namespace std;
int main()
{
string story, input;
//Loop
while(true)
{
//Get a line from the user
getline(cin, input);
//If it's blank, break this loop
if(input == "\r")
break;
//Add the line to the story
story += input;
}
//While there is a '<' in the story
int begin;
while((begin = story.find("<")) != string::npos)
{
//Get the category from between '<' and '>'
int end = story.find(">");
string cat = story.substr(begin + 1, end - begin - 1);
//Ask the user for a replacement
cout << "Give me a " << cat << ": ";
cin >> input;
//While there's a matching category
//in the story
while((begin = story.find("<" + cat + ">")) != string::npos)
{
//Replace it with the user's replacement
story.replace(begin, cat.length()+2, input);
}
}
//Output the final story
cout << endl << story;
return 0;
}

View file

@ -0,0 +1,239 @@
MODULE MADLIB !Messing with COMMON is less convenient.
INTEGER MSG,KBD,INF !I/O unit numbers.
DATA MSG,KBD,INF/6,5,10/ !Output, input, some disc file.
INTEGER LSTASH,NSTASH,MSTASH !Prepare a common text stash.
PARAMETER (LSTASH = 246810, MSTASH = 6666) !LSTASH characters for MSTASH texts.
CHARACTER*(LSTASH) STASH !The pool.
INTEGER ISTASH(MSTASH + 1) !Fingers start positions, and thus end positions by extension.
DATA NSTASH,ISTASH(1)/0,1/ !Empty pool: no entries, first available character is at 1.
INTEGER MANYLINES,MANYTESTS !I also want some lists of texts.
PARAMETER (MANYLINES = 1234) !This is to hold the story.
INTEGER NSTORY,STORY(MANYLINES) !Fingering texts in the stash.
PARAMETER (MANYTESTS = 1234) !Likewise, some target/replacement texts.
INTEGER NTESTS,TARGET(MANYTESTS),REPLACEMENT(MANYTESTS) !Thus.
DATA NSTORY,NTESTS/0,0/ !No story lines, and no tests.
INTEGER STACKLIMIT !A recursion limit.
PARAMETER (STACKLIMIT = 28) !This should suffice.
CONTAINS
SUBROUTINE CROAK(GASP) !A dying remark.
CHARACTER*(*) GASP !The last words.
WRITE (MSG,*) "Oh dear." !Shock.
WRITE (MSG,*) GASP !Aargh!
STOP "How sad." !Farewell, cruel world.
END SUBROUTINE CROAK !Farewell...
SUBROUTINE SHOWSTASH(BLAH,I) !One might be wondering.
CHARACTER*(*) BLAH !An annotation.
INTEGER I !The desired stashed text.
WRITE (MSG,1) BLAH,I,STASH(ISTASH(I):ISTASH(I + 1) - 1) !Whee!
1 FORMAT (A,': Text(',I0,')="',A,'"') !Hopefully, helpful.
END SUBROUTINE SHOWSTASH !Ah, debugging.
INTEGER FUNCTION EATTEXT(IN) !Add a text to STASH and finger it.
Co-opts the as-yet unused space in STASH as its scratchpad.
INTEGER IN !Input from this I/O unit number.
INTEGER I,N,L !Fingers.
I = ISTASH(NSTASH + 1)!First available position in STASH.
N = LSTASH - I + 1 !Number of characters yet unused.
IF (N.LT.666) CALL CROAK("Insufficient STASH space remains!")
READ (IN,1,END = 66) L,STASH(I:I + MIN(L,N) - 1) !Calculated during the read.
1 FORMAT (Q,A) !Obviously, Q = character count incoming, A = accept all of them.
L = I + MIN(L,N) - 1 !The last character read.
10 IF (L.LT.I) GO TO 66 !A blank line! Deemed end-of-file.
IF (ICHAR(STASH(L:L)).LE.ICHAR(" ")) THEN !A trailing space?
L = L - 1 !Yes. Pull back.
GO TO 10 !And try again.
END IF !So much for trailing spaces.
IF (NSTASH.GE.MSTASH) CALL CROAK("Too many texts!")
NSTASH = NSTASH + 1 !Admit another text.
ISTASH(NSTASH + 1) = L + 1 !The start point of the following text.
EATTEXT = NSTASH !STASH(ISTASH(n):ISTASH(n + 1) - 1) has text n.
RETURN !All well.
66 EATTEXT = 0 !Sez: "No text".
END FUNCTION EATTEXT !Rather odd side effects.
INTEGER FUNCTION ADDSTASH(TEXT) !Appends an arbitrary text to the pool of stashed texts.
CHARACTER*(*) TEXT !The stuff.
INTEGER I !A finger.
IF (NSTASH.GE.MSTASH) CALL CROAK("The text pool is crowded!") !Alas.
I = ISTASH(NSTASH + 1) !First unused character.
IF (I + LEN(TEXT).GT.LSTASH) CALL CROAK("Overtexted!") !Alack.
STASH(I:I + LEN(TEXT) - 1) = TEXT !Place.
NSTASH = NSTASH + 1 !Count in another entry.
ISTASH(NSTASH + 1) = I + LEN(TEXT) !The new "first available" position.
ADDSTASH = NSTASH !Pass a finger back to the caller.
END FUNCTION ADDSTASH !Just an integer.
INTEGER FUNCTION ANOTHER(TEXT) !Possibly add TEXT to the table of target texts.
Collects TARGET REPLACEMENT pairs (increasing NTESTS) as directed by INSPECT.
CHARACTER*(*) TEXT !The text of the target.
INTEGER I,IT !Steppers.
ANOTHER = 0 !Possibly, the text is already in the table.
DO I = 1,NTESTS !So, step through the known target texts.
IT = TARGET(I) !Finger a target text.
IF (TEXT.EQ.STASH(ISTASH(IT):ISTASH(IT + 1) - 1)) RETURN !Already have this one.
END DO !Otherwise, try the next.
IF (NTESTS.GE.MANYTESTS) CALL CROAK("Too many tests!") !Oh dear.
NTESTS = NTESTS + 1 !Count in another.
TARGET(NTESTS) = ADDSTASH(TEXT)!Stash its text and get a finger to it.
ANOTHER = NTESTS !My caller will want to know which test.
WRITE (MSG,1) TEXT !Now request the replacement text.
1 FORMAT ("Enter your text for ",A,": ",$) !Obviously, the $ indicates "no new line".
REPLACEMENT(NTESTS) = EATTEXT(KBD) !Zero for "no text".
END FUNCTION ANOTHER !Produces entries for TARGET and REPLACEMENT.
SUBROUTINE INSPECT(X) !Examine text number X for the special <...> sequence.
Calls for inspection of REPLACEMENT texts as well, should ANOTHER report a new entry.
INTEGER X !Fingers the text in STASH via ISTASH(X).
INTEGER MARK !Recalls where the < was found.
INTEGER IT,NEW !Fingers to entries in STASH.
INTEGER I !A stepper.
INTEGER SP,STACK(STACKLIMIT) !Prepare for some recursion.
SP = 1 !Start with the starter.
STACK(1) = X !Stack up.
DO WHILE(SP.GT.0) !While texts are yet uninspected,
IT = STACK(SP) !Finger one.
SP = SP - 1 !Working down the stack.
MARK = 0 !Uninitialised variables are bad.
DO I = ISTASH(IT),ISTASH(IT + 1) - 1!Step through the stashed text.
IF (STASH(I:I).EQ."<") THEN !Is it the starter?
MARK = I !Yes. Remember where it is.
ELSE IF (STASH(I:I).EQ.">") THEN !The ender?
IF (MARK.LE.0) CALL CROAK("A > with no preceeding <!") !Bah.
NEW = ANOTHER(STASH(MARK:I)) !Consider the spanned text.
IF (NEW.GT.0) THEN !If that became a new table entry,
IF (SP.GE.STACKLIMIT) CALL CROAK("Stack overflow!") !Its replacement is to be inspected.
SP = SP + 1 !But I'm still busy with the current text.
STACK(SP) = REPLACEMENT(NEW) !So, stack it for later.
END IF !So much for that <...> apparition.
MARK = 0 !Be ready to check afresh for the next.
END IF !So much for that character.
END DO !On to the next.
END DO !So much for that stacked entry.
END SUBROUTINE INSPECT !WRITESTORY will rescan the story lines.
SUBROUTINE READSTORY(IN)!Read and stash the lines.
INTEGER IN !Input from here.
INTEGER LINE !A finger to the story line.
10 LINE = EATTEXT(IN) !So, grab a line.
IF (LINE.GT.0) THEN !A live line?
NSTORY = NSTORY + 1 !Yes.Count it in.
STORY(NSTORY) = LINE !Save it in the story list.
CALL INSPECT(LINE) !Look for trouble as well.
GO TO 10 !And go for the next line.
END IF !Oh for while (Line:=EatText(in)) > 0 do SaveAndInspect(Line);
END SUBROUTINE READSTORY!Simple enough, anyway.
SUBROUTINE WRITESTORY(WIDTH) !Applying the replacements, with replacement replacement too.
Co-opts the as-yet unused space in STASH as its output scratchpad.
Can't rely on changing the index and bounds of a DO-loop on the fly.
INTEGER WIDTH
INTEGER LINE,IT,I,J !Steppers.
INTEGER L,L0,N !Fingers.
INTEGER TAIL,MARK,LAST !Scan choppers.
INTEGER SP,STACKI(STACKLIMIT),STACKL(STACKLIMIT) !Ah, recursion.
L0 = ISTASH(NSTASH + 1) !The first available place in the stash.
L = L0 - 1 !Syncopation for my output finger.
LL:DO LINE = 1,NSTORY !Step through the lines of the story.
SP = 0 !Start with the task in hand.
IT = STORY(LINE) !Finger the stashed line.
LAST = ISTASH(IT + 1) - 1 !Find its last character in STASH.
I = ISTASH(IT) !Find its first character in STASH.
TAIL = I - 1 !Syncopation. No text from this line yet.
IF (STASH(I:I).LE." ") THEN !The line starts with a space?
CALL BURP !Yes. Flush, so as to start a new paragraph.
ELSE IF (LINE.GT.1) THEN !Otherwise, the line is a continuation.
L = L + 1 !So, squeeze in a space as a separator.
STASH(L:L) = " " !Since its text follows on.
END IF !Now for the content of the line.
666 II:DO WHILE(I.LE.LAST) !Step along its text.
IF (STASH(I:I).EQ."<") THEN !Trouble starter?
MARK = I !Yes. Remember where.
ELSE IF (STASH(I:I).EQ.">") THEN !The corresponding ender?
CALL APPEND(TAIL + 1,MARK - 1) !Waiting text up to the mark.
JJ:DO J = 1,NTESTS !Step through the target texts.
IT = TARGET(J) !Finger one.
IF (STASH(ISTASH(IT):ISTASH(IT + 1) - 1) !Its stashed text.
1 .EQ.STASH(MARK:I)) THEN !Matches the suspect text?
IT = REPLACEMENT(J) !Yes! Finger the replacement text.
IF (IT.GT.0) THEN !Null replacements can be ignored.
IF (SP.GE.STACKLIMIT) CALL CROAK("StackOverflow!") !Always diff. messages.
SP = SP + 1 !Interrupt the current scan.
STACKI(SP) = I !Remember where we're up to,
STACKL(SP) = LAST !And the end of the text.
I = ISTASH(IT) - 1 !One will be added shortly, at JJ+1.
LAST = ISTASH(IT + 1) - 1 !Preempt the scan-in-progress.
END IF !To work along the replacement text.
EXIT JJ !Found the target, so the search is finished.
END IF !Otherwise,
END DO JJ !Try the next target text.
TAIL = I !Normal text resumes at TAIL + 1.
END IF !Enough analysis of that character from the story line.
I = I + 1 !The next to consider.
END DO II !Perhaps we've finished this text.
IF (SP.GT.0) THEN !Yes! But, were we interrupted in a previous scan?
CALL APPEND(TAIL + 1,LAST)!Yes! Roll the tail of the just-finished scan.
TAIL = STACKI(SP) !The stacked value of I was fingering a >.
LAST = STACKL(SP) !And this was the end of the text.
SP = SP - 1 !So we've recovered where the scan was.
I = TAIL + 1 !And this is the next to look at.
GO TO 666 !Proceed to do so.
END IF !But if all is unstacked,
CALL APPEND(TAIL + 1,LAST) !Don't forget the tail end.
END DO LL !On to the next story line.
CALL BURP !Any waiting text must be less than WIDTH.
CONTAINS !Some assistants, defined after usage...
SUBROUTINE APPEND(IST,LST) !Has access to L.
INTEGER IST,LST !To copy STASH(IST:LST) to the scratchpad.
INTEGER N !The number of characters to copy.
N = LST - IST + 1 !So find out.
IF (N.LE.0) RETURN !Avoid relying on zero-length action.
IF (L + N.GT.LSTASH) CALL CROAK("Out of stash!") !Oh dear.
STASH(L + 1:L + N) = STASH(IST:LST) !There they go.
L = L + N !Advance my oputput finger.
IF (L - L0 + 1.GE.WIDTH) CALL BURP !Enough to be going on with?
END SUBROUTINE APPEND !Few invocations, if with tricky parameters.
SUBROUTINE BURP !Flushes forth up to WIDTH characters.
INTEGER N,W,L1 !And slides any remnant back.
N = L - L0 + 1 !So, how many characters are waiting?
IF (N.LE.WIDTH) THEN !Too many for one line of output?
L1 = L !Nope. Roll the lot.
ELSE !Otherwise, a partial flush.
W = L0 + WIDTH - 1 !Last character that can be fitted into WIDTH.
DO L1 = W,L0,-1 !Look for a good split.
IF (STASH(L1:L1).LE." ") EXIT !Like, at a space.
END DO !Keep winding back.
IF (L1.LE.L0) L1 = W !No pleasing split found. Just roll a full width.
END IF !Ready to roll.
WRITE (MSG,"(A)") STASH(L0:L1) !Thus!
IF (N.LE.WIDTH) THEN !If the whole text was written,
L = L0 - 1 !Then there is no text in the scratchpad.
ELSE !If only L0:L1 were written of L0:L,
W = L0 + L - L1 - 1 !How far will the remaining text extend?
STASH(L0:W) = STASH(L1 + 1:L) !Shift it.
L = W !Finger the last used character position.
END IF !One trim is enough, even if the scracchpad contains multiple widths' worth..
END SUBROUTINE BURP !Since I don't want to flush the lot.
END SUBROUTINE WRITESTORY !Just a sequence of lines.
END MODULE MADLIB !Enough of that.
PROGRAM MADLIBBER !See, for example, https://en.wikipedia.org/wiki/Mad_Libs
USE MADLIB
WRITE (MSG,1) !It's polite to explain.
10FORMAT ("Reads a story in template form, containing special ",
1 "entries such as <dog's name> amongst the text.",/,
2 "You will be invited to supply a replacement text for each "
3 "such entry, as encountered,",/,
4 "after which the story will be presented with your ",
5 "substitutions made.",//,
6 "Here goes... Reading file Madlib.txt",/)
OPEN(INF,STATUS="OLD",ACTION="READ",FORM="FORMATTED",
1 FILE = "Madlib.txt")
CALL READSTORY(INF)
CLOSE(INF)
WRITE (MSG,*)
WRITE (MSG,*) " Righto!"
WRITE (MSG,*)
CALL WRITESTORY(66)
END

View file

@ -0,0 +1,14 @@
require 'general/misc/prompt regex'
madlib=:3 :0
smoutput 'Please enter the story template'
smoutput 'See http://rosettacode.org/wiki/Mad_Libs for details'
t=.''
while.#l=.prompt '' do. t=.t,l,LF end.
repl=. ~.'<[^<>]*>' rxall t
for_bef. repl do.
aft=. prompt (}.}:;bef),': '
t=.t rplc bef,<aft
end.
t
)

View file

@ -0,0 +1,11 @@
madlib''
Please enter the story template
See http://rosettacode.org/wiki/Mad_Libs for details
<name> went for a walk in the park. <he or she>
found a <noun>. <name> decided to take it home.
name: Jill
he or she: she
noun: rock
Jill went for a walk in the park. she
found a rock. Jill decided to take it home.

View file

@ -1,59 +0,0 @@
coclass 'AA'
NB. associative array
create=: verb define
empty KEYS=: DATA =: ''
)
destroy=: codestroy
put=: dyad define NB. DATUM put KEY
DATA=: DATA , < x
KEYS=: KEYS , < y
EMPTY
)
get=: verb define NB. get KEY
(KEYS (i. <) y) {:: DATA
)
cocurrent'base'
get =: dyad define NB. OBJECT get KEY
try.
get__x y
catch.
smoutput '?' ,~ ": y
RV =. 1!:1<1
RV put__x y
RV
end.
)
STORY =: 0 :0
<name> went for a walk in the park. <he or she>
found a <noun>. <name> decided to take it home.
)
madlib =: verb define NB. madlib STORY
I =. y i. '<'
if. I = # y do. y return. end. NB. no substitutions
HEAD =. I {. y
TAIL =. I }. y
A =. }. (<;.1~ '<'&=) '<' , TAIL NB. the story is parsed by '<'
B =. (({. ; }.)~ >:@:i.&'>')&> A
NB.+-----------+------------------------------+
NB.|<name> | went for a walk in the park. |
NB.+-----------+------------------------------+
NB.|<he or she>| found a |
NB.+-----------+------------------------------+
NB.|<noun> |. |
NB.+-----------+------------------------------+
NB.|<name> | decided to take it home. |
NB.+-----------+------------------------------+
AA =. conew'AA'
create__AA''
SUBSTITUTIONS =. AA&get&.> _ 1 {. B
codestroy__AA''
HEAD , ; SUBSTITUTIONS ,. 0 1 }. B
)

View file

@ -1,14 +1,11 @@
filename = InputString["Enter the filename of the story template: "];
text = Import[filename];
listofblanks = StringCases[text, RegularExpression["<[^>]+>"]] // Union;
listofanswers = {};
Do[
answer = InputString["Enter a/an: " <> listofblanks[[i]]];
AppendTo[listofanswers, answer];
, {i, 1, Length[listofblanks]}
]
Do[
text = StringReplace[text, listofblanks[[i]] -> listofanswers[[i]]]
, {i, 1, Length[listofblanks]}
]
text
text = Import[
InputString["Enter the filename of the story template:"]];
answers =
AssociationMap[
InputString[
"Enter a" <>
If[StringMatchQ[#,
"<" ~~ Alternatives @@ Characters["aeiou"] ~~ ___], "n", ""] <>
" " <> StringTrim[#, "<" | ">"] <> ":"] &,
Union[StringCases[text, RegularExpression["<[^>]+>"]]]];
Print[StringReplace[text, Normal[answers]]];

View file

@ -0,0 +1,105 @@
Program Madlib; Uses DOS, crt; {See, for example, https://en.wikipedia.org/wiki/Mad_Libs}
{Reads the lines of a story but which also contain <xxx> sequences. For each value of xxx,
found as the lines of the story are read, a request is made for a replacement text.
The story is then written out with the corresponding replacements made.}
{Concocted by R.N.McLean (whom God preserve), Victoria university, NZ.}
Procedure Croak(gasp: string); {A dying message.}
Begin
Writeln(' Eurghfff...');
Writeln(Gasp);
HALT;
End;
var inf: text; {Drivelstuff.}
const StoryLimit = 66;TableLimit = 65; {Big enough.}
var Story: array[1..StoryLimit] of string; {Otherwise, use a temporary disc file.}
var Target,Replacement: array[1..TableLimit] of string;
var StoryLines,TableCount: integer; {Usage.}
Function Reading(var inf: text;var Aline: string): boolean;
Begin
Aline:='';
Reading:=true;
if eoln(inf) then Reading:=false {Agh! Why can't the read statement return true/false?}
else ReadLn(inf,Aline);
if Aline = '' then Reading:=false; {Specified that a blank line ends the story.}
End;
Procedure Inspect(text: string); Forward;{I'd rather have multi-pass compilation than deal with this.}
Procedure Table(it: string); {Check it as a target, and obtain its replacement.}
var i: integer; {A stepper.}
Begin
for i:=1 to TableCount do if it = Target[i] then exit; {Already in the table?}
if TableCount >= TableLimit then Croak('Too many table entries!'); {No. Room for another?}
inc(TableCount); {Yes.}
Target[TableCount]:=it; {Include the < and > to preclude partial matches.}
write('Enter your text for ',it,': '); {Pretty please?}
readln(Replacement[TableCount]); {Thus.}
Inspect(Replacement[TableCount]); {Enable full utilisation.}
End; {of Table.}
var InDeep: integer; {Counts inspection recursion.}
Procedure Inspect(text: string); {Look for <...> in text.}
var i: integer; {A stepper.}
var mark: integer; {Fingers the latest < in Aline.}
Begin
inc(InDeep); {Supply an opportunity, and fear the possibilities.}
if InDeep > 28 then Croak('Excessive recursion! Inspecting ' + text);
for i:=1 to Length(text) do {Now scan the line for trouble.}
if text[i] = '<' then mark:=i {Trouble starts here? Just mark its place.}
else if text[i] = '>' then {Trouble ends here?}
Table(copy(text,mark,i - mark + 1)); {Deal with it.}
dec(InDeep); {I'm done.}
End; {of Inspect.}
Procedure Swallow(Aline: string); {Add a line to the story, and inspect it for <...>.}
Begin
if StoryLines >= StoryLimit then Croak('Too many lines in the story!'); {Suspicion forever.}
inc(StoryLines); {Otherwise, this is safe.}
Story[StoryLines]:=Aline; {So save another line.}
Inspect(Aline); {Look for any <...> inclusions.}
End; {of Swallow.}
var Rolling: integer; {Counts rolling rolls.}
Procedure Roll(bumf: string); {Write a line, with amendments.}
var last,mark: integer; {Fingers for the scan.}
var hit: string; {Copied once.}
var i,it: integer; {Steppers.}
label hic; {Oh dear.}
Begin
inc(Rolling); {Here I go.}
if Rolling > 28 then Croak('Excessive recursion! Rolling ' + bumf); {Self-expansion is out.}
last:=0; {Where the previous text ended.}
for i:=1 to Length(bumf) do {Scan the text.}
if bumf[i] = '<' then mark:=i {Remember where a <...> starts.}
else if bumf[i] = '>' then {So that when the stopper is found,}
begin {It can be recognised.}
Write(copy(bumf,last + 1,mark - last - 1)); {Text up to the <.}
hit:=copy(bumf,mark,i - mark + 1); {Grab this once.}
for it:=1 to TableCount do {Search my table.}
if Target[it] = hit then {A match?}
begin {Yes!}
Roll(Replacement[it]); {Write this instead.}
goto hic; {There is no "exit loop" style statement.}
end; {"Exit" exits the procedure or function.}
hic:last:=i; {Advance the trailing finger.}
end; {On to the next character.}
Write(copy(bumf,last + 1,Length(bumf) - last)); {Text after the last >, possibly null.}
dec(Rolling); {I'm done.}
if Rolling <= 0 then WriteLn; {And if this is the first level, add a end-of-line.}
End; {of Roll.}
var inname: string; {For a file name.}
var Aline: string; {A scratchpad.}
var i: integer; {A stepper.}
BEGIN
InDeep:=0; {No inspections yet.}
Rolling:=0; {No output.}
inname:=ParamStr(1); {Perhaps the file name is specified as a run-time parameter.}
if inname = '' then inname:='Madlib.txt'; {If not, this will do.}
Assign(inf,inname); Reset(inf); {Open the input file.}
StoryLines:=0; TableCount:=0; {Prepare the counters.}
while reading(inf,Aline) do Swallow(Aline); {Read and inspect the story.}
close(inf); {Finished with input.}
for i:=1 to StoryLines do Roll(Story[i]); {Write the amended story.}
END.

View file

@ -1,4 +1 @@
my $story = slurp(@*ARGS.shift);
my %words;
$story.=subst(/ '<' (.*?) '>' /, { %words{$0} //= prompt "$0? " }, :g );
say $story;
print S:g[ '<' (.*?) '>' ] = %.{$0} //= prompt "$0? " given slurp;

View file

@ -0,0 +1,18 @@
Function mad_libs(s)
Do
If InStr(1,s,"<") <> 0 Then
start_position = InStr(1,s,"<") + 1
end_position = InStr(1,s,">")
parse_string = Mid(s,start_position,end_position-start_position)
WScript.StdOut.Write parse_string & "? "
input_string = WScript.StdIn.ReadLine
s = Replace(s,"<" & parse_string & ">",input_string)
Else
Exit Do
End If
Loop
mad_libs = s
End Function
WScript.StdOut.Write mad_libs("<name> went for a walk in the park. <he or she> found a <noun>. <name> decided to take it home.")
WScript.StdOut.WriteLine