Exercism v3 launches on Sept 1st 2021. Learn more! ๐Ÿš€๐Ÿš€๐Ÿš€
Avatar of jpleboeuf

jpleboeuf's solution

to Bob in the Nim Track

Published at Sep 16 2020 · 0 comments
Instructions
Test suite
Solution

Note:

This exercise has changed since this solution was written.

Bob is a lackadaisical teenager. In conversation, his responses are very limited.

Bob answers 'Sure.' if you ask him a question, such as "How are you?".

He answers 'Whoa, chill out!' if you YELL AT HIM (in all capitals).

He answers 'Calm down, I know what I'm doing!' if you yell a question at him.

He says 'Fine. Be that way!' if you address him without actually saying anything.

He answers 'Whatever.' to anything else.

Bob's conversational partner is a purist when it comes to written communication and always follows normal rules regarding sentence punctuation in English.

Running the tests

To compile and run the tests, just run the following in your exercise directory:

$ nim c -r bob_test.nim

Submitting Exercises

Note that, when trying to submit an exercise, make sure the solution is in the $EXERCISM_WORKSPACE/nim/bob directory.

You can find your Exercism workspace by running exercism debug and looking for the line that starts with Exercises Directory.

Need help?

These guides should help you,

Source

Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. http://pine.fm/LearnToProgram/?Chapter=06

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

bob_test.nim

import unittest
import bob

# version 1.6.0

suite "Bob":
  test "stating something":
    check hey("Tom-ay-to, tom-aaaah-to.") == "Whatever."

  test "shouting":
    check hey("WATCH OUT!") == "Whoa, chill out!"

  test "shouting gibberish":
    check hey("FCECDFCAAB") == "Whoa, chill out!"

  test "asking a question":
    check hey("Does this cryogenic chamber make me look fat?") == "Sure."

  test "asking a numeric question":
    check hey("You are, what, like 15?") == "Sure."

  test "asking gibberish":
    check hey("fffbbcbeab?") == "Sure."

  test "talking forcefully":
    check hey("Hi there!") == "Whatever."

  test "using acronyms in regular speech":
    check hey("It's OK if you don't want to go work for NASA.") == "Whatever."

  test "forceful question":
    check hey("WHAT'S GOING ON?") == "Calm down, I know what I'm doing!"

  test "shouting numbers":
    check hey("1, 2, 3 GO!") == "Whoa, chill out!"

  test "no letters":
    check hey("1, 2, 3") == "Whatever."

  test "question with no letters":
    check hey("4?") == "Sure."

  test "shouting with special characters":
    check hey("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!") == "Whoa, chill out!"

  test "shouting with no exclamation mark":
    check hey("I HATE THE DENTIST") == "Whoa, chill out!"

  test "statement containing question mark":
    check hey("Ending with ? means a question.") == "Whatever."

  test "non-letters with question":
    check hey(":) ?") == "Sure."

  test "prattling on":
    check hey("Wait! Hang on. Are you going to be OK?") == "Sure."

  test "silence":
    check hey("") == "Fine. Be that way!"

  test "prolonged silence":
    check hey("          ") == "Fine. Be that way!"

  test "alternate silence":
    check hey("\t\t\t\t\t\t\t\t\t\t") == "Fine. Be that way!"

  test "multiple line question":
    check hey("\nDoes this cryogenic chamber make me look fat?\nNo.") == "Whatever."

  test "starting with whitespace":
    check hey("         hmmmmmmm...") == "Whatever."

  test "ending with whitespace":
    check hey("Okay if like my  spacebar  quite a bit?   ") == "Sure."

  test "other whitespace":
    check hey("\n\r \t") == "Fine. Be that way!"

  test "non-question ending with whitespace":
    check hey("This is a statement ending with whitespace      ") == "Whatever."
# Enable experimental ``case`` statement macros:
import macros
{.experimental: "caseStmtMacros".}
#
import strutils
import tables

type
  MsgKind = enum
    mkA  # Asking
    mkY  # Yelling
    mkN  # Nothing
  MsgKinds = set[MsgKind]

proc msgKinds(msg: string): MsgKinds =
  var msgKinds: MsgKinds
  if msg.isEmptyOrWhitespace():
    incl(msgKinds, mkN)
  else:
    if msg.strip().endsWith('?'):
      incl(msgKinds, mkA)
    if Letters in msg and msg == msg.toUpperAscii():
      incl(msgKinds, mkY)
  msgKinds

macro match(n: set): untyped =
  # Macro allowing ``case`` statements to deal with ``set``s.
  # It does so by rewriting these ``case`` statements to ``if`` statements.
  # It only matches ``case`` statement's selectors of type ``set``.
  #
  # Produce a new ``if`` statement node:
  result = newTree(nnkIfStmt)
  # Name the ``case`` statement's selector:
  let nSelector = n[0]
  # Iterate over the ``case`` statement's branches:
  for i in 1..<n.len:
    # Name the current ``case`` statement's branch:
    let nBranch = n[i]
    # Switch depending on the kind of the ``case`` statement's part:
    case nBranch.kind:
      # ``if`` statement/expression branch:
      of nnkElifBranch, nnkElse, nnkElifExpr, nnkElseExpr:
        # Just copy these kinds of branch:
        result.add nBranch
      # ``of`` branch:
      of nnkOfBranch:
        # Iterate over the comma-separated list of ``set``s:
        for j in 0..nBranch.len-2:
          # Replace the ``of`` branch by an ``elif`` branch
          #  testing for equality between
          #  the ``set`` in the selector
          #  and the current ``set``
          #  (leveraging the existing equality operator for ``set``s):
          let cond = newCall("==", nSelector, nBranch[j])
          result.add newTree(nnkElifBranch, cond, nBranch[^1])
      else:
          error "'match' cannot handle this node", nBranch
  echo repr result

proc hey*(message: string): string =
  const replyTo = {
      "asking_question": "Sure.",
      "yelling": "Whoa, chill out!",
      "yelling_question": "Calm down, I know what I'm doing!",
      "saying_nothing": "Fine. Be that way!",
      "default": "Whatever.",
    }.toTable
  case msgKinds(message):
    of {mkA}:
      return replyTo["asking_question"]
    of {mkY}:
      return replyTo["yelling"]
    of {mkA} + {mkY}:
      return replyTo["yelling_question"]
    of {mkN}:
      return replyTo["saying_nothing"]
    else:
      return replyTo["default"]

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other peopleโ€™s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?