NCL 205: Comparing and Interpreting Strings

In NCL 204, we took a structured string like:

EMMA:hello

and extracted two separate strings:

#name = "EMMA"
#text = "hello"

Once we have those pieces, we can do more than display them.

We can make decisions based on what they contain.

Comparing strings

We've already used BEQ to compare integers.

For example:

BEQ $five r0 5

branches to $five if the value in r0 is equal to 5.

Strings have their own equality branch: BSEQ.

#name s0

SMOVE #name "EMMA"

BSEQ $emma #name "EMMA"

D.TXT "Someone else"
JUMP $done

$emma
D.TXT "Hello, Emma"

$done
D.BLT

The program displays:

Hello, Emma

BSEQ compares two strings and branches if they are equal.

In this case:

BSEQ $emma #name "EMMA"

asks whether the string stored in #name is equal to "EMMA".

If it is, the next instruction comes from $emma.

If it isn't, execution continues with the instruction below the BSEQ.

Try changing:

SMOVE #name "EMMA"

to:

SMOVE #name "ALICE"

The comparison is now false, so the branch isn't taken.

The program displays:

Someone else

Equal and not equal

There is also BSNEQ.

It branches if two strings are not equal.

For example:

BSNEQ $other #name "EMMA"

branches to $other if #name contains anything other than "EMMA".

The integer and string equality branches correspond closely:

Integers Strings Branch when
BEQ BSEQ equal
BNEQ BSNEQ not equal

The important difference is what they compare.

BEQ and BNEQ compare integer values.

BSEQ and BSNEQ compare strings.

Capitalization matters

String comparisons are case-sensitive.

These strings are different:

EMMA
Emma
emma

For example:

#name s0

SMOVE #name "Emma"

BSEQ $emma #name "EMMA"

D.TXT "No match"
JUMP $done

$emma
D.TXT "Match"

$done
D.BLT

displays:

No match

Sometimes that's exactly what we want.

Other times, capitalization shouldn't matter.

We already know how to handle that.

#name s0

SMOVE #name "Emma"

SUPR #name #name
BSEQ $emma #name "EMMA"

D.TXT "No match"
JUMP $done

$emma
D.TXT "Match"

$done
D.BLT

SUPR changes the value in #name to:

EMMA

before the comparison.

Now the program displays:

Match

Instead of asking BSEQ to perform a different kind of comparison, we change the strings into a consistent form first.

We used the same technique with SFIND in NCL 203.

Interpreting a string

Let's give our extracted strings a more useful job.

Suppose we have:

SAY:Hello!

Using the techniques from NCL 204, we can split that into:

#command = "SAY"
#argument = "Hello!"

For now, suppose that work has already been done:

#command s0
#argument s1

SMOVE #command "SAY"
SMOVE #argument "Hello!"

We can use BSEQ to decide what the command means:

BSEQ $say #command "SAY"

D.TXT "Unknown command"
JUMP $done

$say
D.TXT #argument

$done
D.BLT

Because #command contains "SAY", the program branches to:

$say

and displays:

Hello!

Change:

SMOVE #command "SAY"

to:

SMOVE #command "BANANA"

and neither the string nor BSEQ knows what "BANANA" is supposed to mean.

The program simply doesn't take the branch, so it displays:

Unknown command

BSEQ does not understand commands.

It only compares strings.

The meaning of "SAY" comes from what our program does when that comparison succeeds.

More than one command

One comparison gives us one recognized command.

More comparisons give us more possibilities.

Let's add:

SHOUT
#command s0
#argument s1

SMOVE #command "SHOUT"
SMOVE #argument "Hello!"

BSEQ $say #command "SAY"
BSEQ $shout #command "SHOUT"

D.TXT "Unknown command"
JUMP $done

$say
D.TXT #argument
JUMP $done

$shout
SUPR #argument #argument
D.TXT #argument

$done
D.BLT

The program checks:

BSEQ $say #command "SAY"

That comparison fails, so execution continues.

Then it checks:

BSEQ $shout #command "SHOUT"

That comparison succeeds.

Execution moves to $shout, where:

SUPR #argument #argument

changes the argument to uppercase before displaying it.

The result is:

HELLO!

We have built another chain of decisions, much like the ones we used with numbers in NCL 109.

The first matching branch determines which path the program follows.

If none match, execution reaches:

D.TXT "Unknown command"

which acts as our default path.

Normalizing the command

Our current commands are case-sensitive.

That means:

SAY

works, but:

say

doesn't.

For a command language, we might decide that capitalization shouldn't matter.

We can normalize the command before comparing it:

SUPR #command #command

BSEQ $say #command "SAY"
BSEQ $shout #command "SHOUT"

Now all of these become "SAY" before the comparisons:

SAY
say
Say
sAy

Notice that we only change #command.

We leave #argument alone.

For:

say:Hello, Emma!

we want the command to be interpreted without caring about capitalization, while preserving:

Hello, Emma!

exactly as it was supplied.

Choosing which values to transform can be just as important as choosing the transformation itself.

Putting the pieces together

Now let's combine the splitting from NCL 204 with the comparisons from this lesson.

Our input will have the form:

COMMAND:argument

First, we find and validate the separator.

Then we extract the command and argument.

Finally, we interpret the command.

#separator r0
#length r1
#last r2
#leftEnd r3
#rightStart r4

#command s0
#argument s1

#input "SHOUT:Hello, Emma!"

SFIND #separator #input ":"
BEQ $invalid #separator -1
BEQ $invalid #separator 0

SLEN #length #input
MOVE #last #length
DEC #last

BEQ $invalid #separator #last

MOVE #leftEnd #separator
DEC #leftEnd

MOVE #rightStart #separator
INC #rightStart

SSUB #command #input 0 #leftEnd
SSUB #argument #input #rightStart -1

SUPR #command #command

BSEQ $say #command "SAY"
BSEQ $shout #command "SHOUT"

D.TXT "Unknown command"
JUMP $done

$say
D.TXT #argument
JUMP $done

$shout
SUPR #argument #argument
D.TXT #argument
JUMP $done

$invalid
D.TXT "Invalid input"

$done
D.BLT

With:

#input "SHOUT:Hello, Emma!"

the program displays:

HELLO, EMMA!

Change the input to:

#input "say:Hello, Emma!"

and it displays:

Hello, Emma!

The command was normalized before being interpreted.

The argument wasn't.

Our program now follows a complete sequence:

  1. Find the separator.
  2. Check that the separator is usable.
  3. Extract the command and argument.
  4. Normalize the command.
  5. Compare the command against the commands we recognize.
  6. Choose the corresponding behavior.

None of these steps is especially complicated on its own.

Combining them lets a simple string describe what a program should do.

Try it

Add a third command:

WHISPER:Hello, WORLD!

When the command is WHISPER, display its argument in lowercase.

The result should be:

hello, world!

You'll need another comparison:

BSEQ $whisper #command "WHISPER"

and another path:

$whisper

You have already used the instruction that converts a string to lowercase.

Try your program with:

WHISPER:NCS/E READY

and predict what it will display before running it.

Then add another command:

REPEAT:Hello!

Make it display:

Hello! Hello!

This one doesn't require a new string operation at all.

You already have the argument.

You just need to decide what to do with it.


We can now extract pieces from structured text and choose behavior based on what those pieces contain.

In NCL 206: Building and Formatting Strings, we'll start constructing strings into useful layouts and formats.