S/W Design and Architecture

Domain-Specific Language Pattern

Objectives

During this activity:

This activity helps students develop the following skills, values and attitudes: ability to analyze and synthesize, capacity for identifying and solving problems, and efficient use of computer systems.


Activity Description

This activity can be developed individually or in pairs.

  1. Create a directory called dsl and a subdirectory src within it. Inside the src folder create two files called: jankenpon.rb and jankenpon_test.rb.

    All Ruby source files must start with a comment containing the lab’s title, date, and the authors’ personal information. For example:

    # Domain-Specific Language Pattern
    # Date: 14-Oct-2019
    # Authors:
    #          A00456654 Thursday Rubinstein 
    #          A01160611 Anthony Stark
    
  2. In the jankenpon.rb source file, implement an internal Domain-Specific Language (DSL) that allows you to see the results of a Rock-Paper-Scissors-Lizard-Spock (a.k.a. Jan-ken-pon five-weapon expansion) hand game.

    The rules of this game are as follows:

    • Scissors cut Paper
    • Paper covers Rock
    • Rock crushes Lizard
    • Lizard poisons Spock
    • Spock smashes Scissors
    • Scissors decapitate Lizard
    • Lizard eats Paper
    • Paper disproves Spock
    • Spock vaporizes Rock
    • Rock crushes Scissors

    These rules are summarized in the following image:

    And this is how Sheldon Cooper explains the game on The Big Bang Theory (Season 2, episode 8, “The Lizard–Spock Expansion”):

    Your DSL should support five “literal” values: Rock, Paper, Scissors, Lizard, and Spock. These values can be combined with the plus (+), minus (-) and parenthesis operators to conform complex expressions. Let A and B be expressions. The expression “A + B” returns the winner between A and B, while “A - B” returns the loser. Additionally, whenever the + and - operators execute the rule being applied is displayed on the standard output. Precedence and associativity of the operators follow Ruby’s rules.

    For example, this DSL code:

    show Spock - (Lizard + Paper)
    

    should display the following:

    Lizard eats Paper (winner Lizard)
    Lizard poisons Spock (loser Spock)
    Result = Spock

    The show command is responsible for the last line of the output, showing the result of the expression it takes as input.

    Notice that the result and effect of “A + B” are the same as “B + A”. The same is true for “A - B” and “B - A”.

    It’s possible to have a tie. This code:

    show Spock + Spock
    

    should generate this output:

    Spock tie (winner Spock)
    Result = Spock
  3. The following unit tests verify the correct behavior of your DSL. Place the test class in the jankenpon_test.rb source file.

    # File: jankenpon_test.rb
    
    require 'minitest/autorun'
    require 'stringio'
    require 'jankenpon'
    
    class JakenponTest < Minitest::Test
    
      def setup
        @old_stdout = $stdout
        @out = $stdout = StringIO.new
      end
    
      def teardown
        $stdout = @old_stdout
      end
    
      def test_simple_cases_plus
        assert_equal Scissors, (Scissors + Paper)
        assert_equal Scissors, (Paper + Scissors)
        assert_equal Paper,    (Paper + Rock)
        assert_equal Paper,    (Rock + Paper)
        assert_equal Rock,     (Rock + Lizard)
        assert_equal Rock,     (Lizard + Rock)
        assert_equal Lizard,   (Lizard + Spock)
        assert_equal Lizard,   (Spock + Lizard)
        assert_equal Spock,    (Spock + Scissors)
        assert_equal Spock,    (Scissors + Spock)
        assert_equal Scissors, (Scissors + Lizard)
        assert_equal Scissors, (Lizard + Scissors)
        assert_equal Lizard,   (Lizard + Paper)
        assert_equal Lizard,   (Paper + Lizard)
        assert_equal Paper,    (Paper + Spock)
        assert_equal Paper,    (Spock + Paper)
        assert_equal Spock,    (Spock + Rock)
        assert_equal Spock,    (Rock + Spock)
        assert_equal Rock,     (Rock + Scissors)
        assert_equal Rock,     (Scissors + Rock)
        assert_equal Scissors, (Scissors + Scissors)
        assert_equal Paper,    (Paper + Paper)
        assert_equal Rock,     (Rock + Rock)
        assert_equal Lizard,   (Lizard + Lizard)
        assert_equal Spock,    (Spock + Spock)
      end
    
      def test_simple_cases_minus
        assert_equal Paper,    (Scissors - Paper)
        assert_equal Paper,    (Paper - Scissors)
        assert_equal Rock,     (Paper - Rock)
        assert_equal Rock,     (Rock - Paper)
        assert_equal Lizard,   (Rock - Lizard)
        assert_equal Lizard,   (Lizard - Rock)
        assert_equal Spock,    (Lizard - Spock)
        assert_equal Spock,    (Spock - Lizard)
        assert_equal Scissors, (Spock - Scissors)
        assert_equal Scissors, (Scissors - Spock)
        assert_equal Lizard,   (Scissors - Lizard)
        assert_equal Lizard,   (Lizard - Scissors)
        assert_equal Paper,    (Lizard - Paper)
        assert_equal Paper,    (Paper - Lizard)
        assert_equal Spock,    (Paper - Spock)
        assert_equal Spock,    (Spock - Paper)
        assert_equal Rock,     (Spock - Rock)
        assert_equal Rock,     (Rock - Spock)
        assert_equal Scissors, (Rock - Scissors)
        assert_equal Scissors, (Scissors - Rock)
        assert_equal Scissors, (Scissors - Scissors)
        assert_equal Paper,    (Paper - Paper)
        assert_equal Rock,     (Rock - Rock)
        assert_equal Lizard,   (Lizard - Lizard)
        assert_equal Spock,    (Spock - Spock)
      end
    
      def test_dsl_1
        #---------
        show Spock
        #---------
        assert_equal \
          "Result = Spock\n", @out.string
      end
    
      def test_dsl_2
        #------------------
        show Spock + Lizard
        #------------------
        assert_equal \
          "Lizard poisons Spock (winner Lizard)\n" \
          "Result = Lizard\n", \
          @out.string
      end
    
      def test_dsl_3
        #------------------
        show Spock - Lizard
        #------------------
        assert_equal \
          "Lizard poisons Spock (loser Spock)\n" \
          "Result = Spock\n", \
          @out.string
      end
    
      def test_dsl_4
        #-------------------------
        show Spock + Lizard + Rock
        #-------------------------
        assert_equal \
          "Lizard poisons Spock (winner Lizard)\n" \
          "Rock crushes Lizard (winner Rock)\n" \
          "Result = Rock\n", \
          @out.string
      end
    
      def test_dsl_5
        #---------------------------
        show Spock + (Lizard + Rock)
        #---------------------------
        assert_equal \
          "Rock crushes Lizard (winner Rock)\n" \
          "Spock vaporizes Rock (winner Spock)\n" \
          "Result = Spock\n", \
          @out.string
      end
    
      def test_dsl_6
        #--------------------------------------------
        show Rock + Paper + Scissors + Lizard + Spock
        #--------------------------------------------
        assert_equal \
          "Paper covers Rock (winner Paper)\n" \
          "Scissors cut Paper (winner Scissors)\n" \
          "Scissors decapitate Lizard (winner Scissors)\n" \
          "Spock smashes Scissors (winner Spock)\n" \
          "Result = Spock\n", \
          @out.string
      end
    
      def test_dsl_7
        #--------------------------------------------
        show Rock - Paper - Scissors - Lizard - Spock
        #--------------------------------------------
        assert_equal \
          "Paper covers Rock (loser Rock)\n" \
          "Rock crushes Scissors (loser Scissors)\n" \
          "Scissors decapitate Lizard (loser Lizard)\n" \
          "Lizard poisons Spock (loser Spock)\n" \
          "Result = Spock\n", \
          @out.string
      end
    
      def test_dsl_8
        #-------------------------------------------------
        show((Rock + Paper) - (Scissors + Lizard) + Spock)
        #-------------------------------------------------
        assert_equal \
          "Paper covers Rock (winner Paper)\n" \
          "Scissors decapitate Lizard (winner Scissors)\n" \
          "Scissors cut Paper (loser Paper)\n" \
          "Paper disproves Spock (winner Paper)\n" \
          "Result = Paper\n", \
          @out.string
      end
    
      def test_dsl_9
        #---------------------------------------------
        show Paper + ((Spock + Paper) - Lizard + Rock)
        #---------------------------------------------
        assert_equal \
          "Paper disproves Spock (winner Paper)\n" \
          "Lizard eats Paper (loser Paper)\n" \
          "Paper covers Rock (winner Paper)\n" \
          "Paper tie (winner Paper)\n" \
          "Result = Paper\n", \
          @out.string
      end
    
    end
    
  4. Write and generate your program’s documentation as described in: Documenting Ruby Programs.

Deliverables

Create a compressed tarball file with the full contents of the dsl directory (including the generated HTML documentation in the doc subdirectory). Call this file dsl.tgz. From a terminal, you can use the following command to create this file (make sure to run it at the same level where the dsl folder resides):

tar czf dsl.tgz dsl

Upload Instructions

To deliver the dsl.tgz file, please provide the following information:

Request PIN

Only one team member needs to upload the file.

Due date is Monday, October 14.

Evaluation

This activity will be evaluated using the following criteria:

50% Implementation of program requirements.
50% Documentation.
1 The program and/or documentation was plagiarized.