Wednesday, July 8, 2015

Arduino Retro Computer: BASIC Interpreter (Let and If)

The following two statements, Let and If, are going to wrap up the minimum I need in my BASIC Interpreter. I'm looking forward to making an Arduino Retro Computer v1.0 post!


Let Statement


The Let Statement is how all variables are assigned - both initial assignment and any changes. Some of the options in the Let statement for integers and floats are:

= <value>  assign the value
++  increment by 1
--  decrement by 1
+= <value>  increment by value
-= <value>  decrement by value
rnd <value>  pick a random value between 0 and value

Strings only support the = <value> option.

Ultimately I'll make it so that if the first word in the command string doesn't match any of the predefined statements, then the interpreter will just consider that unknown word as a variable name (like other languages).

  bool parseProgramString(char *programString)
  {
    .
    .
    else if (!strncmp(programString, "let ", 4))
    {
      parseProgramLet(programString + 4);
    }
    .
    .
  }

  bool parseProgramLet(char *commandString)
  {
    // Up to the first 10 characters (or the first space) is the variable name.
    char variableName[sizeOfVariableName+1];
    memset(variableName, 0, sizeOfVariableName+1);
    for (int variableNameLoop = 0; variableNameLoop < sizeOfVariableName; variableNameLoop++)
    {
      if (*commandString == 0)
      {
        // Should not have reached null terminate yet, fail out.
        advanceNextProgramMemoryAddress();
        return false;
      }
      else if (*commandString == ' ')
      {
        // Done with variable name.
        commandString++;
        break;      
      }
      else
      {
        variableName[variableNameLoop] = *commandString;
        commandString++;
      }
    }

    // Skip over empty spaces.
    while (*commandString == ' ')
    {
      commandString++;
    }
   
    // The remaining characters are the value.
    switch (getProgramVariableType(variableName))
    {
      case stringType:
        {
          char stringValue[sizeOfCommandArray];
          memset(stringValue, 0, sizeOfCommandArray);
          if (!strncmp(commandString, "=", 1))
          {
            commandString++;
            strcpy(stringValue, commandString);
          }
          programVariableWriteString(variableName, stringValue);
        }
        break;
      case integerType:
        {
          long integerValue;
          if (!strcmp(commandString, "++"))
          {
            // Increment the variable value by 1.
            if (programVariableReadInt32(variableName, &integerValue))
            {
              // Successfully found integer value, increment it.
              integerValue ++;
            }
            else
            {
              // Integer not found, just assign to 1.
              integerValue = 1;
            }
          }
          else if (!strncmp(commandString, "+=", 2))
          {
            // Addition operation.
            // Skip past "+=" string.
            commandString+=2;

            // Skip over empty spaces.
            while (*commandString == ' ')
            {
              commandString++;
            }

            // Read the existing value of the integer.
            if (!programVariableReadInt32(variableName, &integerValue))
            {
              // Integer not found, consider it 0.
              integerValue = 0;
            }
            // Then read the amount to add from the command and add it.
            long valueToAdd = 0;
            if (!programVariableReadInt32(commandString, &valueToAdd))
            {
              // Variable not found, interpret the string as a number.
              valueToAdd = strtol(commandString, &commandString, 10);
            }
            integerValue += valueToAdd;
          }
          else if (!strcmp(commandString, "--"))
          {
            // Decrement the variable value by 1.
            if (programVariableReadInt32(variableName, &integerValue))
            {
              // Successfully found integer value, decrement it.
              integerValue --;
            }
            else
            {
              // Integer not found, just assign to -1.
              integerValue = -1;
            }
          }
          else if (!strncmp(commandString, "-=", 2))
          {
            // Subtraction operation.
            // Skip past "-=" string.
            commandString+=2;

            // Skip over empty spaces.
            while (*commandString == ' ')
            {
              commandString++;
            }

            // Read the existing value of the integer.
            if (!programVariableReadInt32(variableName, &integerValue))
            {
              // Integer not found, consider it 0.
              integerValue = 0;
            }
            // Then read the amount to subtract from the command and subtract it.
            long valueToSubtract = 0;
            if (!programVariableReadInt32(commandString, &valueToSubtract))
            {
              // Variable not found, interpret the string as a number.
              valueToSubtract = strtol(commandString, &commandString, 10);
            }
            integerValue -= valueToSubtract;
          }
          else if (!strncmp(commandString, "rnd", 3))
          {
            // Get a random number.
            // Skip past the "rnd" string.
            commandString+=3;
            // Assign a random value between 0 and the given number.
            integerValue = random(strtol(commandString, &commandString, 10));
          }
          else if (!strncmp(commandString, "=", 1))
          {
            // Specific value given.
            commandString++;
            integerValue = strtol(commandString, &commandString, 10);
          }
          // Write value to the variable.
          programVariableWriteInt32(variableName, integerValue);
        }
        break;
      case floatType:
        {
          float floatValue;
          if (!strcmp(commandString, "++"))
          {
            // Increment the variable value by 1.
            if (programVariableReadFloat32(variableName, &floatValue))
            {
              // Successfully found float value, increment it.
              floatValue ++;
            }
            else
            {
              // Float not found, just assign to 1.0.
              floatValue = 1.0;
            }
          }
          else if (!strncmp(commandString, "+=", 2))
          {
            // Addition operation.
            // Skip past "+=" string.
            commandString+=2;

            // Skip over empty spaces.
            while (*commandString == ' ')
            {
              commandString++;
            }

            // Read the existing value of the float.
            if (!programVariableReadFloat32(variableName, &floatValue))
            {
              // Float not found, consider it 0.0.
              floatValue = 0.0;
            }
            // Then read the amount to add from the command and add it.
            float valueToAdd = 0.0;
            if (!programVariableReadFloat32(commandString, &valueToAdd))
            {
              // Variable not found, interpret the string as a number.
              valueToAdd = strtod(commandString, &commandString);
            }
            floatValue += valueToAdd;
          }
          else if (!strcmp(commandString, "--"))
          {
            // Decrement the variable value by 1.
            if (programVariableReadFloat32(variableName, &floatValue))
            {
              // Successfully found float value, decrement it.
              floatValue --;
            }
            else
            {
              // Float not found, just assign to -1.0.
              floatValue = -1.0;
            }
          }
          else if (!strncmp(commandString, "-=", 2))
          {
            // Subtraction operation.
            // Skip past "-=" string.
            commandString+=2;

            // Skip over empty spaces.
            while (*commandString == ' ')
            {
              commandString++;
            }

            // Read the existing value of the float.
            if (!programVariableReadFloat32(variableName, &floatValue))
            {
              // Float not found, consider it 0.
              floatValue = 0.0;
            }
            // Then read the amount to subtract from the command and subtract it.
            float valueToSubtract = 0.0;
            if (!programVariableReadFloat32(commandString, &valueToSubtract))
            {
              // Variable not found, interpret the string as a number.
              valueToSubtract = strtod(commandString, &commandString);
            }
            floatValue -= valueToSubtract;
          }
          else if (!strncmp(commandString, "rnd", 3))
          {
            // Get a random number.
            // Skip past the "rnd" string.
            commandString+=3;
            // Assign a random value between 0 and the given number.
            floatValue = (float)random(strtol(commandString, &commandString, 10));
          }
          else if (!strncmp(commandString, "=", 1))
          {
            // Specific value given.
            commandString++;
            floatValue = (float)strtod(commandString, &commandString);
          }
          // Write value to the variable.
          programVariableWriteFloat32(variableName, floatValue);
        }
        break;
    }

    advanceNextProgramMemoryAddress();
   
    return true;
  }



If Statement


My If statements are a little non-standard now, but they are functional. Rather than having a closing "End If", they will just execute the next line of code if the condition is true or skip it if the condition is false. To have an If condition that executes several lines of code would require the program to goto another section of code then goto back. Less than ideal, but it's something I can improve later.

The If statement does have the standard functionality of comparing with = (or ==), <=, <, >=, and != (or <>).

Also, I know my parseProgramIf function is a monster. It's on the to-do list to refactor the code. :-)

  enum comparisonType
  {
    comparisonEquals = 0,
    comparisonNotEquals = 1,
    comparisonLessThanEquals = 2,
    comparisonGreaterThanEquals = 3,
    comparisonLessThan = 4,
    comparisonGreaterThan = 5
  };

  bool parseProgramString(char *programString)
  {
    .
    .
    else if (!strncmp(programString, "if ", 3))
    {
      parseProgramIf(programString + 3);
    }
    .
    .
  }

  bool parseProgramIf(char *commandString)
  {
    // Up to the first 10 characters (or the first space) is the variable name.
    char variableName[sizeOfVariableName+1];
    memset(variableName, 0, sizeOfVariableName+1);
    for (int variableNameLoop = 0; variableNameLoop < sizeOfVariableName; variableNameLoop++)
    {
      if (*commandString == 0)
      {
        // Should not have reached null terminate yet, fail out.
        advanceNextProgramMemoryAddress();
        advanceNextProgramMemoryAddress();
        return false;
      }
      else if (*commandString == ' ')
      {
        // Done with variable name.
        commandString++;
        break;      
      }
      else
      {
        variableName[variableNameLoop] = *commandString;
        commandString++;
      }
    }

    // Skip over empty spaces.
    while (*commandString == ' ')
    {
      commandString++;
    }
   
    int comparisonType = comparisonEquals; // Default to equals.
    if (!strncmp(commandString, "==", 2))
    {
      comparisonType = comparisonEquals;
      commandString +=2;
    }
    else if (!strncmp(commandString, "=", 1))
    {
      comparisonType = comparisonEquals;
      commandString ++;
    }
    else if (!strncmp(commandString, "!=", 2))
    {
      comparisonType = comparisonNotEquals;
      commandString +=2;
    }
    else if (!strncmp(commandString, "<>", 2))
    {
      comparisonType = comparisonNotEquals;
      commandString +=2;
    }
    else if (!strncmp(commandString, "<=", 2))
    {
      comparisonType = comparisonLessThanEquals;
      commandString +=2;
    }
    else if (!strncmp(commandString, ">=", 2))
    {
      comparisonType = comparisonGreaterThanEquals;
      commandString +=2;
    }
    else if (!strncmp(commandString, "<", 1))
    {
      comparisonType = comparisonLessThan;
      commandString ++;
    }
    else if (!strncmp(commandString, ">", 1))
    {
      comparisonType = comparisonGreaterThan;
      commandString ++;
    }
   
    while (*commandString == ' ')
    {
      commandString++;
    }
   
    if (*commandString == 0)
    {
      // Should not have reached null terminate yet, fail out.
      advanceNextProgramMemoryAddress();
      advanceNextProgramMemoryAddress();
      return false;
    }
 
    // The remaining characters are the value.
    bool errorDetected = false;
    bool matchFound = false;
    switch (getProgramVariableType(variableName))
    {
      case stringType:
        char stringValue1[sizeOfCommandArray];
        if (programVariableReadString(variableName, stringValue1))
        {
          char stringValue2[sizeOfCommandArray];
          if (!programVariableReadString(commandString, stringValue2))
          {
            // Could not find a variable with the string given,
            // use the string itself.
            strcpy(stringValue2, commandString);
          }
          switch (comparisonType)
          {
            case comparisonEquals: // ==
              if (!strcmp(stringValue2, stringValue1))
              {
                matchFound = true;
              }
              break;
            case comparisonNotEquals: // !=
              if (strcmp(stringValue2, stringValue1))
              {
                matchFound = true;
              }
              break;
          }
        }
        else
        {
          errorDetected = true;
        }
        break;
      case integerType:
        long integerValue1;
        if (programVariableReadInt32(variableName, &integerValue1))
        {
          long integerValue2;
          if (!programVariableReadInt32(commandString, &integerValue2))
          {
            // Could not find a variable with the string given,
            // let's get the numeric value of it.
            integerValue2 = strtol(commandString, &commandString, 10);
          }
         
          switch (comparisonType)
          {
            case comparisonEquals: // ==
              if (integerValue1 == integerValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonNotEquals: // !=
              if (integerValue1 != integerValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonLessThanEquals: // <=
              if (integerValue1 <= integerValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonGreaterThanEquals: // >=
              if (integerValue1 >= integerValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonLessThan: // <
              if (integerValue1 < integerValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonGreaterThan: // >
              if (integerValue1 > integerValue2)
              {
                matchFound = true;
              }
              break;
          }
        }
        else
        {
          errorDetected = true;
        }
        break;
      case floatType:
        float floatValue1;
        if (programVariableReadFloat32(variableName, &floatValue1))
        {
          float floatValue2;
          if (!programVariableReadFloat32(commandString, &floatValue2))
          {
            // Could not find a variable with the string given,
            // let's get the numeric value of it.
            floatValue2 = (float)strtod(commandString, &commandString);
          }

          switch (comparisonType)
          {
            case comparisonEquals: // ==
              if (floatValue1 == floatValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonNotEquals: // !=
              if (floatValue1 != floatValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonLessThanEquals: // <=
              if (floatValue1 <= floatValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonGreaterThanEquals: // >=
              if (floatValue1 >= floatValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonLessThan: // <
              if (floatValue1 < floatValue2)
              {
                matchFound = true;
              }
              break;
            case comparisonGreaterThan: // >
              if (floatValue1 > floatValue2)
              {
                matchFound = true;
              }
              break;
          }
        }
        else
        {
          errorDetected = true;
        }
        break;
      default:
        errorDetected = true;
        break;
    }
   
    if (errorDetected)
    {
      addOutputLine("-- Error if condition. --");
      advanceNextProgramMemoryAddress();
    }
    else
    {
      if (matchFound)
      {
        // Match
        // Advance to the next memory address and execute it.
        advanceNextProgramMemoryAddress();
        programExecuteCurrentMemoryAddress();
      }
      else
      {
        // No Match
        // Advance to the next memory address then skip over it.
        advanceNextProgramMemoryAddress();
        advanceNextProgramMemoryAddress();
      }
    }

    return true;
  }

Let & If Statement Example


Simple loop...


10 let i% = 0
20 let i% ++
30 print i%
40 if i% < 10
50 goto 20


Pick a random phrase...


10 let phraseNum% rnd 5
20 if phraseNum% = 0
25 let phraseStr$ =Good. Bad. I'm the guy with the gun.
30 if phraseNum% = 1
35 let phraseStr$ =Klaatu Barada Nikto
40 if phraseNum% = 2
45 let phraseStr$ =Hail to the king, baby.
50 if phraseNum% = 3
55 let phraseStr$ =I'll swallow your soul!
60 if phraseNum% = 4
65 let phraseStr$ =Whoa right there spinach chin!
70 if phraseNum% = 5
75 let phraseStr$ =Gimme some sugar, baby.
80 print phraseStr$


If statement that executes several lines...


10 let distance! = 10000.0
20 if distance! > 1000.0
30 goto 100
40 print "Returned from if code!"
50 end
100 print "If condition is true."
110 print "Let's execute several lines of code."
120 goto 40



Copyright (c) 2015 Clinton Kam
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

No comments:

Post a Comment