Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Tiny BASIC Interpreter and Compiler Project
  3.  * Interpreter module
  4.  *
  5.  * Released as Public Domain by Damian Gareth Walker 2019
  6.  * Created: 23-Aug-2019
  7.  */
  8.  
  9.  
  10. /* included headers */
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include "interpret.h"
  15. #include "errors.h"
  16. #include "options.h"
  17. #include "statement.h"
  18.  
  19.  
  20. /* forward declarations */
  21. static int interpret_expression (ExpressionNode *expression);
  22. static void interpret_statement (StatementNode *statement);
  23.  
  24.  
  25. /*
  26.  * Data Definitions
  27.  */
  28.  
  29. /* The GOSUB Stack */
  30. typedef struct gosub_stack_node GosubStackNode;
  31. typedef struct gosub_stack_node {
  32.   ProgramLineNode *program_line; /* the line following the GOSUB */
  33.   GosubStackNode *next; /* stack node for the previous GOSUB */
  34. } GosubStackNode;
  35.  
  36. /* private data */
  37. typedef struct interpreter_data {
  38.   ProgramNode *program; /* the program to interpret */
  39.   ProgramLineNode *line; /* current line we're executing */
  40.   GosubStackNode *gosub_stack; /* the top of the GOSUB stack */
  41.   int gosub_stack_size; /* number of entries on the GOSUB stack */
  42.   int variables [26]; /* the numeric variables */
  43.   int stopped; /* set to 1 when an END is encountered */
  44.   ErrorHandler *errors; /* the error handler */
  45.   LanguageOptions *options; /* the language options */
  46. } InterpreterData;
  47.  
  48. /* convenience variables */
  49. static Interpreter *this; /* the object we are working with */
  50.  
  51.  
  52. /*
  53.  * Private Methods
  54.  */
  55.  
  56.  
  57. /*
  58.  * Evaluate a factor for the interpreter
  59.  * params:
  60.  *   FactorNode*   factor   the factor to evaluate
  61.  */
  62. static int interpret_factor (FactorNode *factor) {
  63.  
  64.   /* local variables */
  65.   int result_store = 0; /* result of factor evaluation */
  66.  
  67.   /* check factor class */
  68.   switch (factor->class) {
  69.  
  70.     /* a regular variable */
  71.     case FACTOR_VARIABLE:
  72.       result_store = this->priv->variables [factor->data.variable - 1]
  73.         * (factor->sign == SIGN_POSITIVE ? 1 : -1);
  74.       break;
  75.  
  76.     /* an integer constant */
  77.     case FACTOR_VALUE:
  78.       result_store = factor->data.value
  79.         * (factor->sign == SIGN_POSITIVE ? 1 : -1);
  80.       break;
  81.  
  82.     /* an expression */
  83.     case FACTOR_EXPRESSION:
  84.       result_store = interpret_expression (factor->data.expression)
  85.         * (factor->sign == SIGN_POSITIVE ? 1 : -1);
  86.       break;
  87.  
  88.     /* this only happens if the parser has failed in its duty */
  89.     default:
  90.       this->priv->errors->set_code
  91.         (this->priv->errors, E_INVALID_EXPRESSION, 0, this->priv->line->label);
  92.   }
  93.  
  94.   /* check the result and return it*/
  95.   if (result_store < -32768 || result_store > 32767)
  96.     this->priv->errors->set_code
  97.       (this->priv->errors, E_OVERFLOW, 0, this->priv->line->label);
  98.   return result_store;
  99. }
  100.  
  101. /*
  102.  * Evaluate a term for the interpreter
  103.  * params:
  104.  *   TermNode*   term   the term to evaluate
  105.  */
  106. static int interpret_term (TermNode *term) {
  107.  
  108.   /* local variables */
  109.   int result_store; /* the partial evaluation */
  110.   RightHandFactor *rhfactor; /* pointer to successive rh factor nodes */
  111.   int divisor; /* used to check for division by 0 before attempting */
  112.  
  113.   /* calculate the first factor result */
  114.   result_store = interpret_factor (term->factor);
  115.   rhfactor = term->next;
  116.  
  117.   /* adjust store according to successive rh factors */
  118.   while (rhfactor && ! this->priv->errors->get_code (this->priv->errors)) {
  119.     switch (rhfactor->op) {
  120.       case TERM_OPERATOR_MULTIPLY:
  121.         result_store *= interpret_factor (rhfactor->factor);
  122.         if (result_store < -32768 || result_store > 32767)
  123.           this->priv->errors->set_code
  124.             (this->priv->errors, E_OVERFLOW, 0, this->priv->line->label);
  125.         break;
  126.       case TERM_OPERATOR_DIVIDE:
  127.         if ((divisor = interpret_factor (rhfactor->factor)))
  128.           result_store /= divisor;
  129.         else
  130.           this->priv->errors->set_code
  131.             (this->priv->errors, E_DIVIDE_BY_ZERO, 0, this->priv->line->label);
  132.         break;
  133.       default:
  134.         break;
  135.     }
  136.     rhfactor = rhfactor->next;
  137.   }
  138.  
  139.   /* return the result */
  140.   return result_store;
  141. }
  142.  
  143. /*
  144.  * Evaluate an expression for the interpreter
  145.  * params:
  146.  *   ExpressionNode*   expression   the expression to evaluate
  147.  */
  148. static int interpret_expression (ExpressionNode *expression) {
  149.  
  150.   /* local variables */
  151.   int result_store; /* the partial evaluation */
  152.   RightHandTerm *rhterm; /* pointer to successive rh term nodes */
  153.  
  154.   /* calculate the first term result */
  155.   result_store = interpret_term (expression->term);
  156.   rhterm = expression->next;
  157.  
  158.   /* adjust store according to successive rh terms */
  159.   while (rhterm && ! this->priv->errors->get_code (this->priv->errors)) {
  160.     switch (rhterm->op) {
  161.       case EXPRESSION_OPERATOR_PLUS:
  162.         result_store += interpret_term (rhterm->term);
  163.         if (result_store < -32768 || result_store > 32767)
  164.           this->priv->errors->set_code
  165.             (this->priv->errors, E_OVERFLOW, 0, this->priv->line->label);
  166.         break;
  167.       case EXPRESSION_OPERATOR_MINUS:
  168.         result_store -= interpret_term (rhterm->term);
  169.         if (result_store < -32768 || result_store > 32767)
  170.           this->priv->errors->set_code
  171.             (this->priv->errors, E_OVERFLOW, 0, this->priv->line->label);
  172.         break;
  173.       default:
  174.         break;
  175.     }
  176.     rhterm = rhterm->next;
  177.   }
  178.  
  179.   /* return the result */
  180.   return result_store;
  181. }
  182.  
  183. /*
  184.  * Find a program line given its label
  185.  * returns:
  186.  *   ProgramLineNode*   the program line found
  187.  */
  188. static ProgramLineNode *find_label (int jump_label) {
  189.  
  190.   /* local variables */
  191.   ProgramLineNode
  192.     *ptr, /* a line we're currently looking at */
  193.     *found = NULL; /* the line if found */
  194.  
  195.   /* do the search */
  196.   for (ptr = this->priv->program->first; ptr && ! found; ptr = ptr->next)
  197.     if (ptr->label == jump_label)
  198.       found = ptr;
  199.     else if (ptr->label >= jump_label
  200.       && this->priv->options->get_line_numbers (this->priv->options)
  201.         != LINE_NUMBERS_OPTIONAL)
  202.       found = ptr;
  203.  
  204.   /* check for errors and return what was found */
  205.   if (! found)
  206.     this->priv->errors->set_code
  207.       (this->priv->errors, E_INVALID_LINE_NUMBER, 0, this->priv->line->label);
  208.   return found;
  209. }
  210.  
  211.  
  212. /*
  213.  * Level 1 Routines
  214.  */
  215.  
  216.  
  217. /*
  218.  * Initialise the variables
  219.  */
  220. static void initialise_variables (void) {
  221.   int count; /* counter for this->priv->variables */
  222.   for (count = 0; count < 26; ++count) {
  223.     this->priv->variables [count] = 0;
  224.   }
  225. }
  226.  
  227. /*
  228.  * Interpret a LET statement
  229.  * params:
  230.  *   LetStatementNode*   letn   the LET statement details
  231.  */
  232. void interpret_let_statement (LetStatementNode *letn) {
  233.   this->priv->variables [letn->variable - 1]
  234.     = interpret_expression (letn->expression);
  235.   this->priv->line = this->priv->line->next;
  236. }
  237.  
  238. /*
  239.  * Interpret an IF statement
  240.  * params:
  241.  *   IfStatementNode*   ifn   the IF statement details
  242.  */
  243. void interpret_if_statement (IfStatementNode *ifn) {
  244.  
  245.   /* local variables */
  246.   int
  247.     left, /* result of the left-hand expression */
  248.     right, /* result of the right-hand expression */
  249.     comparison; /* result of the comparison between the two */
  250.  
  251.   /* get the expressions */
  252.   left = interpret_expression (ifn->left);
  253.   right = interpret_expression (ifn->right);
  254.  
  255.   /* make the comparison */
  256.   switch (ifn->op) {
  257.     case RELOP_EQUAL: comparison = (left == right); break;
  258.     case RELOP_UNEQUAL: comparison = (left != right); break;
  259.     case RELOP_LESSTHAN: comparison = (left < right); break;
  260.     case RELOP_LESSOREQUAL: comparison = (left <= right); break;
  261.     case RELOP_GREATERTHAN: comparison = (left > right); break;
  262.     case RELOP_GREATEROREQUAL: comparison = (left >= right); break;
  263.   }
  264.  
  265.   /* perform the conditional statement */
  266.   if (comparison && ! this->priv->errors->get_code (this->priv->errors))
  267.     interpret_statement (ifn->statement);
  268.   else
  269.     this->priv->line = this->priv->line->next;
  270. }
  271.  
  272. /*
  273.  * Interpret a GOTO statement
  274.  * params:
  275.  *   GotoStatementNode*   goton   the GOTO statement details
  276.  */
  277. void interpret_goto_statement (GotoStatementNode *goton) {
  278.   int label; /* the line label to go to */
  279.   label = interpret_expression (goton->label);
  280.   if (! this->priv->errors->get_code (this->priv->errors))
  281.     this->priv->line = find_label (label);
  282. }
  283.  
  284. /*
  285.  * Interpret a GOSUB statement
  286.  * params:
  287.  *   GosubStatementNode*   gosubn   the GOSUB statement details
  288.  */
  289. void interpret_gosub_statement (GosubStatementNode *gosubn) {
  290.  
  291.   /* local variables */
  292.   GosubStackNode *gosub_node; /* indicates the program line to return to */
  293.   int label; /* the line label to go to */
  294.  
  295.   /* create the new node on the GOSUB stack */
  296.   if (this->priv->gosub_stack_size < this->priv->options->get_gosub_limit
  297.     (this->priv->options)) {
  298.     gosub_node = malloc (sizeof (GosubStackNode));
  299.     gosub_node->program_line = this->priv->line->next;
  300.     gosub_node->next = this->priv->gosub_stack;
  301.     this->priv->gosub_stack = gosub_node;
  302.     ++this->priv->gosub_stack_size;
  303.   } else
  304.     this->priv->errors->set_code (this->priv->errors,
  305.       E_TOO_MANY_GOSUBS, 0, this->priv->line->label);
  306.  
  307.   /* branch to the subroutine requested */
  308.   if (! this->priv->errors->get_code (this->priv->errors))
  309.     label = interpret_expression (gosubn->label);
  310.   if (! this->priv->errors->get_code (this->priv->errors))
  311.     this->priv->line = find_label (label);
  312. }
  313.  
  314. /*
  315.  * Interpret a RETURN statement
  316.  */
  317. void interpret_return_statement (void) {
  318.  
  319.   /* local variables */
  320.   GosubStackNode *gosub_node; /* node popped off the GOSUB stack */
  321.  
  322.   /* return to the statement following the most recent GOSUB */
  323.   if (this->priv->gosub_stack) {
  324.     this->priv->line = this->priv->gosub_stack->program_line;
  325.     gosub_node = this->priv->gosub_stack;
  326.     this->priv->gosub_stack = this->priv->gosub_stack->next;
  327.     free (gosub_node);
  328.     --this->priv->gosub_stack_size;
  329.   }
  330.  
  331.   /* no GOSUBs led here, so raise an error */
  332.   else
  333.     this->priv->errors->set_code
  334.       (this->priv->errors, E_RETURN_WITHOUT_GOSUB, 0, this->priv->line->label);
  335. }
  336.  
  337. /*
  338.  * Interpret a PRINT statement
  339.  * params:
  340.  *   PrintStatementNode*   printn   the PRINT statement details
  341.  */
  342. void interpret_print_statement (PrintStatementNode *printn) {
  343.  
  344.   /* local variables */
  345.   OutputNode *outn; /* current output node */
  346.   int
  347.     items = 0, /* counter ensures runtime errors appear on a new line */
  348.     result; /* the result of an expression */
  349.  
  350.   /* print each of the output items */
  351.   outn = printn->first;
  352.   while (outn) {
  353.     switch (outn->class) {
  354.       case OUTPUT_STRING:
  355.         printf ("%s", outn->output.string);
  356.         ++items;
  357.         break;
  358.       case OUTPUT_EXPRESSION:
  359.         result = interpret_expression (outn->output.expression);
  360.         if (! this->priv->errors->get_code (this->priv->errors)) {
  361.           printf ("%d", result);
  362.           ++items;
  363.         }
  364.         break;
  365.     }
  366.     outn = outn->next;
  367.   }
  368.  
  369.   /* print the linefeed */
  370.   if (items)
  371.     printf ("\n");
  372.   this->priv->line = this->priv->line->next;
  373. }
  374.  
  375. /*
  376.  * Interpret an INPUT statement
  377.  * params:
  378.  *   InputStatementNode*   inputn   the INPUT statement details
  379.  */
  380. void interpret_input_statement (InputStatementNode *inputn) {
  381.  
  382.   /* local variables */
  383.   VariableListNode *variable; /* current variable to input */
  384.   int
  385.     value, /* value input from the user */
  386.     sign = 1, /* the default sign */
  387.     ch = 0; /* character from the input stream */
  388.  
  389.   /* input each of the variables */
  390.   variable = inputn->first;
  391.   while (variable) {
  392.     do {
  393.       if (ch == '-') sign = -1; else sign = 1;
  394.       ch = getchar ();
  395.     } while (ch < '0' || ch > '9');
  396.     value = 0;
  397.     do {
  398.       value = 10 * value + (ch - '0');
  399.       if (value * sign < -32768 || value * sign > 32767)
  400.         this->priv->errors->set_code
  401.           (this->priv->errors, E_OVERFLOW, 0, this->priv->line->label);
  402.       ch = getchar ();
  403.     } while (ch >= '0' && ch <= '9'
  404.       && ! this->priv->errors->get_code (this->priv->errors));
  405.     this->priv->variables [variable->variable - 1] = sign * value;
  406.     variable = variable->next;
  407.   }
  408.  
  409.   /* advance to the next statement when done */
  410.   this->priv->line = this->priv->line->next;
  411. }
  412.  
  413.  
  414. /*
  415.  * Interpret an individual statement
  416.  * params:
  417.  *   StatementNode*   statement   the statement to interpret
  418.  */
  419. void interpret_statement (StatementNode *statement) {
  420.  
  421.   /* skip comments */
  422.   if (! statement) {
  423.     this->priv->line = this->priv->line->next;
  424.     return;
  425.   }
  426.  
  427.   /* interpret real statements */
  428.   switch (statement->class) {
  429.     case STATEMENT_NONE:
  430.       break;
  431.     case STATEMENT_LET:
  432.       interpret_let_statement (statement->statement.letn);
  433.       break;
  434.     case STATEMENT_IF:
  435.       interpret_if_statement (statement->statement.ifn);
  436.       break;
  437.     case STATEMENT_GOTO:
  438.       interpret_goto_statement (statement->statement.goton);
  439.       break;
  440.     case STATEMENT_GOSUB:
  441.       interpret_gosub_statement (statement->statement.gosubn);
  442.       break;
  443.     case STATEMENT_RETURN:
  444.       interpret_return_statement ();
  445.       break;
  446.     case STATEMENT_END:
  447.        this->priv->stopped = 1;
  448.      break;
  449.     case STATEMENT_PRINT:
  450.       interpret_print_statement (statement->statement.printn);
  451.       break;
  452.     case STATEMENT_INPUT:
  453.       interpret_input_statement (statement->statement.inputn);
  454.       break;
  455.     default:
  456.       printf ("Statement type %d not implemented.\n", statement->class);
  457.   }
  458. }
  459.  
  460. /*
  461.  * Interpret program starting from a particular line
  462.  * params:
  463.  *   ProgramLineNode*   program_line   the starting line
  464.  */
  465. static void interpret_program_from (ProgramLineNode *program_line) {
  466.   this->priv->line = program_line;
  467.   while (this->priv->line
  468.     && ! this->priv->stopped
  469.     && ! this->priv->errors->get_code (this->priv->errors))
  470.     interpret_statement (this->priv->line->statement);
  471. }
  472.  
  473.  
  474. /*
  475.  * Public Methods
  476.  */
  477.  
  478.  
  479. /*
  480.  * Interpret the program from the beginning
  481.  * params:
  482.  *   Interpreter*   interpreter   the interpreter to use
  483.  *   ProgramNode*   program       the program to interpret
  484.  */
  485. static void interpret (Interpreter *interpreter, ProgramNode *program) {
  486.   this = interpreter;
  487.   this->priv->program = program;
  488.   initialise_variables ();
  489.   interpret_program_from (this->priv->program->first);
  490. }
  491.  
  492. /*
  493.  * Destroy the interpreter
  494.  * params:
  495.  *   Interpreter*   interpreter   the doomed interpreter
  496.  */
  497. static void destroy (Interpreter *interpreter) {
  498.   if (interpreter) {
  499.     if (interpreter->priv)
  500.       free (interpreter->priv);
  501.     free (interpreter);
  502.   }
  503. }
  504.  
  505.  
  506. /*
  507.  * Constructors
  508.  */
  509.  
  510.  
  511. /*
  512.  * Constructor
  513.  * returns:
  514.  *   Interpreter*   the new interpreter
  515.  */
  516. Interpreter *new_Interpreter (ErrorHandler *errors, LanguageOptions *options) {
  517.  
  518.   /* allocate memory */
  519.   this = malloc (sizeof (Interpreter));
  520.   this->priv = malloc (sizeof (InterpreterData));
  521.  
  522.   /* initialise methods */
  523.   this->interpret = interpret;
  524.   this->destroy = destroy;
  525.  
  526.   /* initialise properties */
  527.   this->priv->gosub_stack = NULL;
  528.   this->priv->gosub_stack_size = 0;
  529.   this->priv->stopped = 0;
  530.   this->priv->errors = errors;
  531.   this->priv->options = options;
  532.  
  533.   /* return the new object */
  534.   return this;
  535. }
  536.