CLS PRINT " +--------------------------------------------+ " PRINT " | | " PRINT " | ITALIAN RESTAURANT | " PRINT " | | " PRINT " | A very silly program indeed. | " PRINT " | | " PRINT " | Based on Douglas R. Hofstadter's article | " PRINT " | 'Lisp: Recursion and Generality' from | " PRINT " | his Scientific American column | " PRINT " | 'Metamagical Themas' (April, 1983) | " PRINT " | | " PRINT " +--------------------------------------------+ "' //// ------------------------------------------------------------- /// Main program // --------------------------------------------------------------- RANDOMISE read_rules print_menu (60) REPEAT PRINT "-o-o-o-o-"' INPUT "What would you like for a starter? " : food$ PRINT '"An excellent choice! And for the main course, allow me to suggest:"' REPEAT // Keep looping until there are no more rules to apply, // or the string is over five hundred characters long. UNTIL NOT apply_rule (food$) OR LEN food$ > 500 print_wrapped_lines (food$, 60) PRINT '"I do hope you enjoy your meal."' UNTIL FALSE //// ------------------------------------------------------------- /// Procedures and functions // --------------------------------------------------------------- // Count_rules% - returns the number of 'production rules' there are FUNC count_rules% CLOSED n% := 0 RESTORE WHILE NOT EOD DO READ r$, e$ n% := n% + 1 ENDWHILE RESTORE RETURN n% ENDFUNC count_rules% // Read_rules - reads the production rules into two string arrays PROC read_rules num_rules := count_rules% DIM rule$ (num_rules) DIM expansion$ (num_rules) FOR i := 1 TO num_rules DO READ rule$ (i), expansion$ (i) NEXT i ENDPROC read_rules // Print_menu - prints a list containing the first part of each rule PROC print_menu (width) PRINT "TODAY'S SPECIALS:"' menu$ := rule$ (1) FOR i := 2 TO num_rules DO menu$ := menu$ + ", " + rule$ (i) NEXT i print_wrapped_lines (menu$, width) PRINT ENDPROC print_menu // Apply_rule - applies a random rule to the given string // and returns TRUE, or just returns FALSE if no rule fits. FUNC apply_rule (REF food$) CLOSED IMPORT num_rules, rule$, expansion$ DIM p% (num_rules) FOR i := 1 TO num_rules DO p% (i) := i NEXT i permute (p% (), num_rules) FOR i := 1 TO num_rules DO n := p% (i) r$ := rule$ (n) pos% := r$ IN food$ IF pos% THEN food$ (pos% : pos% + LEN r$ - 1) := expansion$ (n) RETURN TRUE ENDIF NEXT i RETURN FALSE ENDFUNC apply_rule //// ------------------------------------------------------------- /// Utility procedures // --------------------------------------------------------------- // These procedures are not specific to this particular program; // they are just useful in general. // Permute - randomly shuffles a list of integers PROC permute (REF num% (), size%) CLOSED IF size% > 1 THEN swap (num% (size%), num% (RND size%)) permute (num% (), size% - 1) ENDIF ENDPROC permute // Swap - swaps two integers PROC swap (REF a%, REF b%) CLOSED c% := a% a% := b% b% := c% ENDPROC swap // Print_wrapped_lines - prints a long string over several lines PROC print_wrapped_lines (s$, width) CLOSED pos := 0 temp$ := "" WHILE TRUE DO next_space := " " IN s$ IF next_space = 0 THEN PRINT temp$; s$ RETURN ENDIF temp$ := temp$ + s$ (: next_space) s$ := s$ (next_space + 1 :) pos := pos + next_space IF pos >= width THEN PRINT temp$ temp$ := "" pos := 0 ENDIF ENDWHILE ENDPROC print_wrapped_lines //// ------------------------------------------------------------- /// DATA statements // --------------------------------------------------------------- // The DATA statements below form a 'production system'; // the first string in each pair produces the second. // It's also a 'context-free grammar' - a language for // describing bizarre meals! DATA Tomatoes, "tomatoes on macaroni (and tomatoes only), exquisitely spiced" DATA Macaroni, "macaroni and cheese (a repast of Naples, Italy)" DATA Repast, "rather extraordinary pasta and sauce" DATA Cheese, "cheddar, havarti, emmenthaler (especially sharp emmenthaler)" DATA Sharp, "strong, hearty and rather pungent" DATA Spiced, "sweetly pickled in cheese endive dressing" DATA Endive, "egg noodles, dipped in vinegar eggnog" DATA Noodles, "noodles (oodles of delicious linguini), elegantly served" DATA Linguini, "lambchops (including noodles), gotten usually in Northern Italy" DATA Pasta, "pasta and sauce (that's all!)" DATA All!, "a luscious lunch" DATA Sauce, "shad and unusual coffee (eccellente!)" DATA Shad, "spaghetti, heated al dente" DATA Spaghetti, "standard pasta, always good, hot especially (twist, then ingest)" DATA Coffee, "choice of fine flavours, especially espresso" DATA Espresso, "excellent, strong, powerful, rich espresso, suppressing sleep outrageously" DATA Basta!, "belly all stuffed (tummy ache!)" DATA Lambchops, "lasagne and meat balls, casually heaped onto pasta sauce" DATA Lasagne, "linguini and sauce and garlic (noodles everywhere!)" DATA Rhubarb, "ravioli, heated under butter and rhubarb (basta!)" DATA Ravioli, "rigatoni and vognole in oil, lavishly introduced" DATA Rigatoni, "rich Italian gnocchi and tomatoes (or noodles instead)" DATA Gnocchi, "garlic noodles over crisp cheese, heated immediately" DATA Garlic, "green and red lasagne in cheese" // These are the rules used in Hofstadter's article. // Add more if you want (you don't have to stick to pseudo-Italian food, either).