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