Skip to content

Commit

Permalink
Add proverb exercise (#296)
Browse files Browse the repository at this point in the history
keiravillekode authored Jan 18, 2025
1 parent aaab18b commit 5d4f2a6
Showing 9 changed files with 367 additions and 8 deletions.
24 changes: 16 additions & 8 deletions config.json
Original file line number Diff line number Diff line change
@@ -386,6 +386,14 @@
"prerequisites": [],
"difficulty": 5
},
{
"slug": "bottle-song",
"name": "Bottle Song",
"uuid": "b6d90e4b-9f17-459b-aff3-2716e49ccb9c",
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "diamond",
"name": "Diamond",
@@ -410,6 +418,14 @@
"prerequisites": [],
"difficulty": 5
},
{
"slug": "proverb",
"name": "Proverb",
"uuid": "c4bb45c9-9c9d-4a56-933b-df7aa2d45945",
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "raindrops",
"name": "Raindrops",
@@ -579,14 +595,6 @@
"practices": [],
"prerequisites": [],
"difficulty": 10
},
{
"slug": "bottle-song",
"name": "Bottle Song",
"uuid": "b6d90e4b-9f17-459b-aff3-2716e49ccb9c",
"practices": [],
"prerequisites": [],
"difficulty": 5
}
],
"foregone": [
6 changes: 6 additions & 0 deletions exercises/practice/proverb/.docs/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Hints

## General

- The `$t0-9` registers can be used to temporarily store values
- The instructions specify which registers are used as input and output
15 changes: 15 additions & 0 deletions exercises/practice/proverb/.docs/instructions.append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Instructions append

## Input format

Each list of inputs is represented as a null-terminated string, with a newline character at the end of each input.

An example would be `"nail\nshoe\nhorse\nrider\nmessage\nbattle\nkingdom\n"`

## Registers

| Register | Usage | Type | Description |
| -------- | ------------ | ------- | -------------------------------------------------------------- |
| `$a0` | input | address | null-terminated input string with newline after each input |
| `$a1` | input/output | address | null-terminated output string |
| `$t0-9` | temporary | any | for temporary storage |
19 changes: 19 additions & 0 deletions exercises/practice/proverb/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Instructions

For want of a horseshoe nail, a kingdom was lost, or so the saying goes.

Given a list of inputs, generate the relevant proverb.
For example, given the list `["nail", "shoe", "horse", "rider", "message", "battle", "kingdom"]`, you will output the full text of this proverbial rhyme:

```text
For want of a nail the shoe was lost.
For want of a shoe the horse was lost.
For want of a horse the rider was lost.
For want of a rider the message was lost.
For want of a message the battle was lost.
For want of a battle the kingdom was lost.
And all for the want of a nail.
```

Note that the list of inputs may vary; your solution should be able to handle lists of arbitrary length and content.
No line of the output text should be a static, unchanging string; all should vary according to the input given.
19 changes: 19 additions & 0 deletions exercises/practice/proverb/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"keiravillekode"
],
"files": {
"solution": [
"impl.mips"
],
"test": [
"runner.mips"
],
"example": [
".meta/example.mips"
]
},
"blurb": "For want of a horseshoe nail, a kingdom was lost, or so the saying goes. Output the full text of this proverbial rhyme.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/For_Want_of_a_Nail"
}
95 changes: 95 additions & 0 deletions exercises/practice/proverb/.meta/example.mips
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# | Register | Usage | Type | Description |
# | -------- | ------------ | ------- | ---------------------------------------------------------- |
# | `$a0` | input | address | null-terminated input string with newline after each input |
# | `$a1` | input/output | address | null-terminated output string |
# | `$a2` | temporary | address | null-terminated or newline-terminated source string |
# | `$t0` | temporary | byte | character for output |
# | `$t6` | temporary | address | current string |
# | `$t7` | temporary | address | first string |
# | `$t8` | temporary | byte | '\n' newline |
# | `$t9` | temporary | address | return address |

.globl recite

.data

for_want: .asciiz "For want of a "
the: .asciiz " the "
was_lost: .asciiz " was lost.\n"
and_all: .asciiz "And all for the want of a "
stop: .asciiz ".\n"


.text

recite:
move $t7, $a0 # first string
li $t8, '\n'
move $t9, $ra # Save return address
j next

write_line:
la $a2, for_want
jal copy_string

move $a2, $t6
jal copy_line

la $a2, the
jal copy_string

move $a2, $a0
jal copy_line

la $a2, was_lost
jal copy_string

next:
move $t6, $a0 # current string

scan:
lb $t0, 0($a0) # read input byte
addi $a0, $a0, 1 # increment input pointer
bne $t0, $t8, scan # loop until newline

lb $t0, 0($a0) # first byte after newline
bnez $t0, write_line

write_final_line:
la $a2, and_all
jal copy_string

move $a2, $t7 # first string
jal copy_line

la $a2, stop
jal copy_string

jr $t9


copy_string:
# copy string from source $a2 to destination $a1
lb $t0, 0($a2) # load source byte
sb $t0, 0($a1) # write byte to destination
addi $a2, $a2, 1 # increment souce pointer
addi $a1, $a1, 1 # increment destination pointer
bnez $t0, copy_string # repeat until we have reached null terminator

subi $a1, $a1, 1 # decrement destination pointer,
# ready to append other strings
jr $ra


copy_line:
# copy line from source $a2 to destination $a1
# assume $t8 is '\n'
lb $t0, 0($a2) # load source byte
sb $t0, 0($a1) # write byte to destination
addi $a2, $a2, 1 # increment souce pointer
addi $a1, $a1, 1 # increment destination pointer
bne $t0, $t8, copy_line # repeat until we have reached '\n' line terminator

subi $a1, $a1, 1 # decrement destination pointer,
# ready to append other strings
jr $ra
29 changes: 29 additions & 0 deletions exercises/practice/proverb/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[e974b73e-7851-484f-8d6d-92e07fe742fc]
description = "zero pieces"
include = false

[2fcd5f5e-8b82-4e74-b51d-df28a5e0faa4]
description = "one piece"

[d9d0a8a1-d933-46e2-aa94-eecf679f4b0e]
description = "two pieces"

[c95ef757-5e94-4f0d-a6cb-d2083f5e5a83]
description = "three pieces"

[433fb91c-35a2-4d41-aeab-4de1e82b2126]
description = "full proverb"

[c1eefa5a-e8d9-41c7-91d4-99fab6d6b9f7]
description = "four pieces modernized"
10 changes: 10 additions & 0 deletions exercises/practice/proverb/impl.mips
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# | Register | Usage | Type | Description |
# | -------- | ------------ | ------- | ---------------------------------------------------------- |
# | `$a0` | input | address | null-terminated input string with newline after each input |
# | `$a1` | input/output | address | null-terminated output string |
# | `$t0-9` | temporary | any | for temporary storage |

.globl recite

recite:
jr $ra
158 changes: 158 additions & 0 deletions exercises/practice/proverb/runner.mips
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#
# Test recite with some examples
#
# a0 - inputs string, for callee
# a1 - pointer to output string, for callee
# s0 - num of tests left to run
# s1 - address of inputs string
# s2 - address of expected output string
# s3 - copy of output location
# t0 - char byte of expected output
# t1 - char byte of actual output

.eqv BUFFER_SIZE 400

.data

# number of test cases
n: .word 5
strings: .ascii
"nail\n\0",

"nail\n",
"shoe\n\0",

"nail\n",
"shoe\n",
"horse\n\0",

"nail\n",
"shoe\n",
"horse\n",
"rider\n",
"message\n",
"battle\n",
"kingdom\n\0",

"pin\n",
"gun\n",
"soldier\n",
"battle\n\0"
# expected output values
outs: .ascii
"And all for the want of a nail.\n\0",

"For want of a nail the shoe was lost.\n",
"And all for the want of a nail.\n\0",

"For want of a nail the shoe was lost.\n",
"For want of a shoe the horse was lost.\n",
"And all for the want of a nail.\n\0",

"For want of a nail the shoe was lost.\n",
"For want of a shoe the horse was lost.\n",
"For want of a horse the rider was lost.\n",
"For want of a rider the message was lost.\n",
"For want of a message the battle was lost.\n",
"For want of a battle the kingdom was lost.\n",
"And all for the want of a nail.\n\0",

"For want of a pin the gun was lost.\n",
"For want of a gun the soldier was lost.\n",
"For want of a soldier the battle was lost.\n",
"And all for the want of a pin.\n\0"

failmsg: .asciiz "failed for test inputs\n"
expectedmsg: .asciiz "expected\n"
tobemsg: .asciiz "to be\n"
okmsg: .asciiz "all tests passed"


.text

runner:
lw $s0, n
la $s1, strings
la $s2, outs

li $v0, 9 # code for allocating heap memory
li $a0, BUFFER_SIZE # specify length of longest expected output
syscall
move $s3, $v0 # location of allocated memory is where callee writes result

run_test:
jal clear_output # zero out output location
move $a0, $s1 # load input strings value into a0
move $a1, $s3 # load destination address into a1
jal recite # call subroutine under test
move $a1, $s3
move $s6, $s3 # take copy of output value
move $s7, $s2 # expected output

scan:
lb $t0, 0($s2) # load one byte of the expectation
lb $t1, 0($a1) # load one byte of the actual
bne $t0, $t1, exit_fail # if the two differ, the test has failed
addi $s2, $s2, 1 # point to next expectation byte
addi $a1, $a1, 1 # point to next actual byte
bne $t0, $zero, scan # if one char (and therefore the other) was not null, loop

inputs_scan:
lb $t0, 0($s1)
addi $s1, $s1, 1
bne $t0, $zero, inputs_scan

done_scan:
sub $s0, $s0, 1 # decrement num of tests left to run
bgt $s0, $zero, run_test # if more than zero tests to run, jump to run_test

exit_ok:
la $a0, okmsg # put address of okmsg into a0
li $v0, 4 # 4 is print string
syscall

li $v0, 10 # 10 is exit with zero status (clean exit)
syscall

exit_fail:
la $a0, failmsg # put address of failmsg into a0
li $v0, 4 # 4 is print string
syscall

move $a0, $s1 # print input strings that failed on
li $v0, 4
syscall

la $a0, expectedmsg
li $v0, 4
syscall

move $a0, $s6 # print actual that failed on
li $v0, 4
syscall

la $a0, tobemsg
li $v0, 4
syscall

move $a0, $s7 # print expected value that failed on
li $v0, 4
syscall

li $a0, 1 # set error code to 1
li $v0, 17 # 17 is exit with error
syscall


clear_output:
# zero out output by storing zeros
addi $t0, $s3, BUFFER_SIZE # pointer to end of output buffer

clear:
subi $t0, $t0, 4 # decrement pointer
sw $zero, 0($t0) # store a 0 word
bne $t0, $s3, clear # repeat util we have reached the start of the buffer
jr $ra

# # Include your implementation here if you wish to run this from the MARS GUI.
# .include "impl.mips"

0 comments on commit 5d4f2a6

Please sign in to comment.