The Big Book of Small EK9 Programs
Adapted from Al Sweigart's The Big Book of Small Python Projects: 81 Easy Practice Programs (No Starch Press, 2021; copyright © 2021 Al Sweigart), which he makes freely available under a Creative Commons Attribution-NonCommercial-ShareAlike licence at inventwithpython.com/bigbookpython. Changes were made: each program was ported from Python to EK9 and remodelled in EK9's idioms.
Project 54 — Pig Latin
This is project 54 of the book, ported to EK9. Its chapter is still being written; until then, this page shows the port itself: a run of it, the whole program, and how it keeps to the original.
A sample run
The player's answers are piped in:
Hello world! My name is AL.
Run it with:
ek9 piglatin.ek9 < input.txt
It prints:
Igpay Atinlay (Pig Latin)
By Al Sweigart al@inventwithpython.com
Enter your message:
Ellohay orldway! Ymay amenay isyay ALYAY.
The program
#!ek9 defines module bigbook.piglatin defines constant //A word is: any non-letters, the letters (the shortest run that leaves only //non-letters after it), then any non-letters. \P{L} is "not a letter". WordShape <- /^(?<prefix>\P{L}*)(?<letters>.*?)(?<suffix>\P{L}*)$/ //y counts as a vowel, as in the book. LeadingConsonants <- /^(?<consonants>[^aeiouy]*)(?<rest>.*)$/ defines class //THE input idiom of this book, copied unchanged from lib/LineInput.ek9 (the reasons are //there): read with next(), and every input loop also stops when ended() is true. LineInput stdin as Stdin: Stdin() inputEnded as Boolean: false next() <- rtn as String: stdin.next() if not rtn? inputEnded: true ended() as pure <- rtn as Boolean: Boolean(inputEnded) override operator ? as pure <- rtn as Boolean: true //THE INPUT AND OUTPUT: everything the player sees or types goes through here, worded by Say. Console stdout as Stdout: Stdout() input as LineInput: LineInput() say as Say: Say("en") Console() -> words as Say say: words //A blank line after the introduction is layout, so the Console prints it. showIntroduction() stdout.println(say.introduction()) stdout.println("") //The message to translate; unset only if the input ends first. nextMessage() <- rtn as String: String() stdout.println(say.askMessage()) rtn: input.next() //The translation is computed, not worded: it is shown as the game made it. showTranslated() -> translated as String stdout.println(translated) override operator ? as pure <- rtn as Boolean: true defines function //THE GAME: the translation, with no input or output. Pig Latin's "ay" and "yay" are its //rules, not words to translate. //Title case wins when a word is both title and upper case, as in the book, so a //one-letter "I" comes out "Iyay". restoredCase() as pure -> translated as String original as String <- rtn as String: translated if original == original.title() rtn: translated.title() else if original == original.upperCase() rtn: translated.upperCase() //Move the leading consonants to the end and add "ay"; a word that starts with a vowel //gets "yay" instead. pigLatinLetters() as pure -> letters as String <- rtn as String: String() lower <- letters.lowerCase() consonants <- lower.group(LeadingConsonants, "consonants") rest <- lower.group(LeadingConsonants, "rest") translated <- consonants.empty() <- `${rest}yay` : `${rest}${consonants}ay` rtn: restoredCase(translated, letters) //Punctuation around a word stays where it was; only the letters are translated. pigLatinWord() as pure -> word as String <- rtn as String: word prefix <- word.group(WordShape, "prefix") letters <- word.group(WordShape, "letters") suffix <- word.group(WordShape, "suffix") if not letters.empty() rtn: `${prefix}${pigLatinLetters(letters)}${suffix}` //The book puts a space after every word, the last one included. englishToPigLatin() -> message as String <- rtn as String: "" for word in message.trim().split(/\s+/) rtn += `${pigLatinWord(word)} ` defines function //THE language of the words, copied unchanged from lib/Language.ek9: BIGBOOK_LANG when it //is set (fr, zh, ...), otherwise en. language() <- rtn as String: EnvVars().get("BIGBOOK_LANG") ?: "en" //THE WORDS: every word the player sees. defines text for "en" Say introduction() `Igpay Atinlay (Pig Latin) By Al Sweigart al@inventwithpython.com` askMessage() "Enter your message:" defines program //Pig Latin (Big Book project 54). Translates one line of English into Pig Latin. The game //decides; the Console asks and tells, in the player's words. PigLatin() //A language with no words of its own is unset: ?: falls back to English. console <- Console(Say(language()) ?: Say("en")) console.showIntroduction() if message <- console.nextMessage() console.showTranslated(englishToPigLatin(message))
About this port
- Behaviour: reads one line and translates each word: punctuation around the word stays
put, the leading consonants move to the end with "ay" (
ycounts as a vowel, so a word that starts with one gets "yay"), and UPPER or Title case is restored. As in the book, every word is followed by a space, the last one included. The golden translatesHello world! My name is AL.toEllohay orldway! Ymay amenay isyay ALYAY.. - Idioms:
- The book peels non-letters off each end of a word one character at a time in two loops.
Here one regular expression with named groups splits a word into
prefix,lettersandsuffix(\P{L}is "not a letter"), and a second splits the leading consonants from the rest, so there are no index loops and no slicing. - In
restoredCasetitle case wins when a word is both title and upper case, as in the book, soIbecomesIyay. - Every function except the one that builds the sentence is
as pure; that one appends in a loop, which a pure function may not do (E08120).
- The book peels non-letters off each end of a word one character at a time in two loops.
Here one regular expression with named groups splits a word into
- Omitted: the optional
pyperclipclipboard copy.