Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Tiny BASIC Interpreter and Compiler Project
  3.  * Parser module
  4.  *
  5.  * Copyright (C) Damian Gareth Walker 2019
  6.  * Created: 12-Aug-2019
  7.  */
  8.  
  9.  
  10. /* included headers */
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <ctype.h>
  15. #include "common.h"
  16. #include "errors.h"
  17. #include "options.h"
  18. #include "token.h"
  19. #include "tokeniser.h"
  20. #include "parser.h"
  21. #include "expression.h"
  22.  
  23.  
  24. /*
  25.  * Internal Function Declarations
  26.  */
  27.  
  28.  
  29. /* parse_expression() has a forward reference from parse_factor() */
  30. static ExpressionNode *parse_expression (void);
  31.  
  32. /* parse_statement() has a forward reference from parse_if_statement() */
  33. static StatementNode *parse_statement (void);
  34.  
  35.  
  36. /*
  37.  * Data Definitions
  38.  */
  39.  
  40.  
  41. /* private data */
  42. typedef struct parser_data {
  43.   int last_label; /* last line label encountered */
  44.   int current_line; /* the last source line parsed */
  45.   int end_of_file; /* end of file signal */
  46.   Token *stored_token; /* token read ahead */
  47.   TokenStream *stream; /* the input stream */
  48.   ErrorHandler *errors; /* the parse error handler */
  49.   LanguageOptions *options; /* the language options */
  50. } ParserData;
  51.  
  52. /* convenience variables */
  53. static Parser *this; /* the current parser being worked on */
  54.  
  55.  
  56. /*
  57.  * Private methods
  58.  */
  59.  
  60.  
  61. /*
  62.  * Get next token to parse, from read-ahead buffer or tokeniser.
  63.  */
  64. static Token *get_token_to_parse () {
  65.  
  66.   /* local variables */
  67.   Token *token; /* token to return */
  68.  
  69.   /* get the token one way or another */
  70.   if (this->priv->stored_token) {
  71.     token = this->priv->stored_token;
  72.     this->priv->stored_token = NULL;
  73.   } else
  74.     token = this->priv->stream->next (this->priv->stream);
  75.  
  76.   /* store the line, check EOF and return the token */
  77.   this->priv->current_line = token->get_line (token);
  78.   if (token->get_class (token) == TOKEN_EOF)
  79.     this->priv->end_of_file = !0;
  80.   return token;
  81. }
  82.  
  83. /*
  84.  * Parse a factor
  85.  * returns:
  86.  *   FactorNode*   a new factor node holding the parsed data
  87.  */
  88. static FactorNode *parse_factor (void) {
  89.  
  90.   /* local variables */
  91.   Token *token; /* token to read */
  92.   FactorNode *factor = NULL; /* the factor we're building */
  93.   ExpressionNode *expression = NULL; /* any parenthesised expression */
  94.   int start_line; /* the line on which this factor occurs */
  95.  
  96.   /* initialise the factor and grab the next token */
  97.   factor = factor_create ();
  98.   token = get_token_to_parse ();
  99.   start_line = token->get_line (token);
  100.  
  101.   /* interpret a sign */
  102.   if (token->get_class (token) == TOKEN_PLUS
  103.     || token->get_class (token) == TOKEN_MINUS) {
  104.     factor->sign = (token->get_class (token) == TOKEN_PLUS)
  105.       ? SIGN_POSITIVE
  106.       : SIGN_NEGATIVE;
  107.     token->destroy (token);
  108.     token = get_token_to_parse ();
  109.   }
  110.  
  111.   /* interpret a number */
  112.   if (token->get_class (token) == TOKEN_NUMBER) {
  113.     factor->class = FACTOR_VALUE;
  114.     factor->data.value = atoi (token->get_content (token));
  115.     if (factor->data.value < -32768 || factor->data.value > 32767)
  116.       this->priv->errors->set_code
  117.         (this->priv->errors, E_OVERFLOW, start_line, this->priv->last_label);
  118.     token->destroy (token);
  119.   }
  120.  
  121.   /* interpret a variable */
  122.   else if (token->get_class (token) == TOKEN_VARIABLE) {
  123.     factor->class = FACTOR_VARIABLE;
  124.     factor->data.variable = (int) *token->get_content (token) & 0x1F;
  125.     token->destroy (token);
  126.   }
  127.  
  128.   /* interpret an parenthesised expression */
  129.   else if (token->get_class (token) == TOKEN_LEFT_PARENTHESIS) {
  130.  
  131.     /* parse the parenthesised expression and complete the factor */
  132.     token->destroy (token);
  133.     expression = parse_expression ();
  134.     if (expression) {
  135.       token = get_token_to_parse ();
  136.       if (token->get_class (token) == TOKEN_RIGHT_PARENTHESIS) {
  137.         factor->class = FACTOR_EXPRESSION;
  138.         factor->data.expression = expression;
  139.       } else {
  140.         this->priv->errors->set_code
  141.           (this->priv->errors, E_MISSING_RIGHT_PARENTHESIS, start_line,
  142.             this->priv->last_label);
  143.         factor_destroy (factor);
  144.         factor = NULL;
  145.         expression_destroy (expression);
  146.       }
  147.       token->destroy (token);
  148.     }
  149.  
  150.     /* clean up after invalid parenthesised expression */
  151.     else {
  152.       this->priv->errors->set_code (this->priv->errors, E_INVALID_EXPRESSION,
  153.         token->get_line (token), this->priv->last_label);
  154.       token->destroy (token);
  155.       factor_destroy (factor);
  156.       factor = NULL;
  157.     }
  158.   }
  159.  
  160.   /* deal with other errors */
  161.   else {
  162.     this->priv->errors->set_code
  163.       (this->priv->errors, E_INVALID_EXPRESSION, token->get_line (token),
  164.         this->priv->last_label);
  165.     factor_destroy (factor);
  166.     token->destroy (token);
  167.     factor = NULL;
  168.   }
  169.  
  170.   /* return the factor */
  171.   return factor;
  172. }
  173.  
  174. /*
  175.  * Parse a term
  176.  * globals:
  177.  *   Token*      stored_token   the token read past the end of the term
  178.  * returns:
  179.  *   TermNode*   a new term node holding the parsed term
  180.  */
  181. static TermNode *parse_term (void) {
  182.  
  183.   /* local variables */
  184.   TermNode *term = NULL; /* the term we're building */
  185.   FactorNode *factor = NULL; /* factor detected */
  186.   RightHandFactor
  187.     *rhptr = NULL, /* previous right-hand factor */
  188.     *rhfactor = NULL; /* right-hand factor detected */
  189.   Token *token = NULL; /* token read while looking for operator */
  190.  
  191.   /* scan the first factor */
  192.   if ((factor = parse_factor ())) {
  193.     term = term_create ();
  194.     term->factor = factor;
  195.     term->next = NULL;
  196.  
  197.     /* look for subsequent factors */
  198.     while ((token = get_token_to_parse ())
  199.       && ! this->priv->errors->get_code (this->priv->errors)
  200.       && (token->get_class (token) == TOKEN_MULTIPLY
  201.       || token->get_class (token) == TOKEN_DIVIDE)) {
  202.  
  203.       /* parse the sign and the factor */
  204.       rhfactor = rhfactor_create ();
  205.       rhfactor->op = token->get_class (token) == TOKEN_MULTIPLY
  206.           ? TERM_OPERATOR_MULTIPLY
  207.           : TERM_OPERATOR_DIVIDE;
  208.       if ((rhfactor->factor = parse_factor ())) {
  209.         rhfactor->next = NULL;
  210.         if (rhptr)
  211.           rhptr->next = rhfactor;
  212.         else
  213.           term->next = rhfactor;
  214.         rhptr = rhfactor;
  215.       }
  216.  
  217.       /* set an error if we read an operator but not a factor */
  218.       else {
  219.         rhfactor_destroy (rhfactor);
  220.         if (! this->priv->errors->get_code (this->priv->errors))
  221.           this->priv->errors->set_code
  222.             (this->priv->errors, E_INVALID_EXPRESSION, token->get_line (token),
  223.               this->priv->last_label);
  224.       }
  225.  
  226.       /* clean up token */
  227.       token->destroy (token);
  228.     }
  229.  
  230.     /* we've read past the end of the term; put the token back */
  231.     this->priv->stored_token = token;
  232.   }
  233.  
  234.   /* return the evaluated term, if any */
  235.   return term;
  236. }
  237.  
  238. /*
  239.  * Parse an expression
  240.  * returns:
  241.  *   ExpressionNode*    the parsed expression
  242.  */
  243. static ExpressionNode *parse_expression (void) {
  244.  
  245.   /* local variables */
  246.   ExpressionNode *expression = NULL; /* the expression we build */
  247.   TermNode *term; /* term detected */
  248.   RightHandTerm
  249.     *rhterm = NULL, /* the right-hand term detected */
  250.     *rhptr = NULL; /* pointer to the previous right-hand term */
  251.   Token *token; /* token read when scanning for right-hand terms */
  252.  
  253.   /* scan the first term */
  254.   if ((term = parse_term ())) {
  255.     expression = expression_create ();
  256.     expression->term = term;
  257.     expression->next = NULL;
  258.  
  259.     /* look for subsequent terms */
  260.     while ((token = get_token_to_parse ())
  261.       && ! this->priv->errors->get_code (this->priv->errors)
  262.       && (token->get_class (token) == TOKEN_PLUS
  263.       || token->get_class (token) == TOKEN_MINUS)) {
  264.  
  265.       /* parse the sign and the factor */
  266.       rhterm = rhterm_create ();
  267.       rhterm->op = token->get_class (token) == TOKEN_PLUS
  268.           ? EXPRESSION_OPERATOR_PLUS
  269.           : EXPRESSION_OPERATOR_MINUS;
  270.       if ((rhterm->term = parse_term ())) {
  271.         rhterm->next = NULL;
  272.         if (rhptr)
  273.           rhptr->next = rhterm;
  274.         else
  275.           expression->next = rhterm;
  276.         rhptr = rhterm;
  277.       }
  278.  
  279.       /* set an error condition if we read a sign but not a factor */
  280.       else {
  281.         rhterm_destroy (rhterm);
  282.         if (! this->priv->errors->get_code (this->priv->errors))
  283.           this->priv->errors->set_code
  284.             (this->priv->errors, E_INVALID_EXPRESSION, token->get_line (token),
  285.               this->priv->last_label);
  286.       }
  287.  
  288.       /* clean up token */
  289.       token->destroy (token);
  290.     }
  291.  
  292.     /* we've read past the end of the term; put the token back */
  293.     this->priv->stored_token = token;
  294.   }
  295.  
  296.   /* return the evaluated expression, if any */
  297.   return expression;
  298. }
  299.  
  300. /*
  301.  * Calculate numeric line label according to language options.
  302.  * This will be used if the line has no label specified.
  303.  * returns:
  304.  *   int                         numeric line label
  305.  */
  306. static int generate_default_label (void) {
  307.   if (this->priv->options->get_line_numbers (this->priv->options)
  308.     == LINE_NUMBERS_IMPLIED)
  309.     return this->priv->last_label + 1;
  310.   else
  311.     return 0;
  312. }
  313.  
  314. /*
  315.  * Validate a line label according to the language options
  316.  * params:
  317.  *   int               label     the numeric label to verify.
  318.  * returns:
  319.  *   int                         !0 if the number is valid, 0 if invalid
  320.  */
  321. static int validate_line_label (int label) {
  322.  
  323.   /* line labels should be non-negative and within the set limit */
  324.   if (label < 0
  325.     || label > this->priv->options->get_line_limit (this->priv->options))
  326.     return 0;
  327.  
  328.   /* line labels should be non-zero unless they're optional */
  329.   if (label == 0
  330.     && this->priv->options->get_line_numbers (this->priv->options)
  331.       != LINE_NUMBERS_OPTIONAL)
  332.     return 0;
  333.  
  334.   /* line labels should be ascending unless they're optional */
  335.   if (label <= this->priv->last_label
  336.     && this->priv->options->get_line_numbers (this->priv->options)
  337.       != LINE_NUMBERS_OPTIONAL)
  338.     return 0;
  339.  
  340.   /* if all the above tests passed, the line label is valid */
  341.   return 1;
  342. }
  343.  
  344. /*
  345.  * Parse a LET statement
  346.  * returns:
  347.  *   StatementNode*          The statement assembled
  348.  */
  349. static StatementNode *parse_let_statement (void) {
  350.  
  351.   /* local variables */
  352.   Token *token; /* tokens read as part of LET statement */
  353.   int line; /* line containing the LET token */
  354.   StatementNode *statement; /* the new statement */
  355.  
  356.   /* initialise the statement */
  357.   statement = statement_create ();
  358.   statement->class = STATEMENT_LET;
  359.   statement->statement.letn = statement_create_let ();
  360.   line = this->priv->stream->get_line (this->priv->stream);
  361.  
  362.   /* see what variable we're assigning */
  363.   token = get_token_to_parse ();
  364.   if (token->get_class (token) != TOKEN_VARIABLE) {
  365.     this->priv->errors->set_code
  366.       (this->priv->errors, E_INVALID_VARIABLE, line, this->priv->last_label);
  367.     statement_destroy (statement);
  368.     token->destroy (token);
  369.     return NULL;
  370.   }
  371.   statement->statement.letn->variable = *token->get_content (token) & 0x1f;
  372.  
  373.   /* get the "=" */
  374.   token->destroy (token);
  375.   token = get_token_to_parse ();
  376.   if (token->get_class (token) != TOKEN_EQUAL) {
  377.     this->priv->errors->set_code
  378.       (this->priv->errors, E_INVALID_ASSIGNMENT, line, this->priv->last_label);
  379.     statement_destroy (statement);
  380.     token->destroy (token);
  381.     return NULL;
  382.   }
  383.   token->destroy (token);
  384.  
  385.   /* get the expression */
  386.   statement->statement.letn->expression = parse_expression ();
  387.   if (! statement->statement.letn->expression) {
  388.     this->priv->errors->set_code
  389.       (this->priv->errors, E_INVALID_EXPRESSION, line, this->priv->last_label);
  390.     statement_destroy (statement);
  391.     return NULL;
  392.   }
  393.  
  394.   /* return the statement */
  395.   return statement;
  396. }
  397.  
  398. /*
  399.  * Parse an IF statement
  400.  * returns:
  401.  *   StatementNode*          The statement to assemble.
  402.  */
  403. static StatementNode *parse_if_statement (void) {
  404.  
  405.   /* local variables */
  406.   Token *token; /* tokens read as part of the statement */
  407.   StatementNode *statement; /* the IF statement */
  408.  
  409.   /* initialise the statement */
  410.   statement = statement_create ();
  411.   statement->class = STATEMENT_IF;
  412.   statement->statement.ifn = statement_create_if ();
  413.  
  414.   /* parse the first expression */
  415.   statement->statement.ifn->left = parse_expression ();
  416.  
  417.   /* parse the operator */
  418.   if (! this->priv->errors->get_code (this->priv->errors)) {
  419.     token = get_token_to_parse ();
  420.     switch (token->get_class (token)) {
  421.     case TOKEN_EQUAL:
  422.       statement->statement.ifn->op = RELOP_EQUAL;
  423.       break;
  424.     case TOKEN_UNEQUAL:
  425.       statement->statement.ifn->op = RELOP_UNEQUAL;
  426.       break;
  427.     case TOKEN_LESSTHAN:
  428.       statement->statement.ifn->op = RELOP_LESSTHAN;
  429.       break;
  430.     case TOKEN_LESSOREQUAL:
  431.       statement->statement.ifn->op = RELOP_LESSOREQUAL;
  432.       break;
  433.     case TOKEN_GREATERTHAN:
  434.       statement->statement.ifn->op = RELOP_GREATERTHAN;
  435.       break;
  436.     case TOKEN_GREATEROREQUAL:
  437.       statement->statement.ifn->op = RELOP_GREATEROREQUAL;
  438.       break;
  439.     default:
  440.       this->priv->errors->set_code
  441.         (this->priv->errors, E_INVALID_OPERATOR, token->get_line (token),
  442.         this->priv->last_label);
  443.     }
  444.     token->destroy (token);
  445.   }
  446.  
  447.   /* parse the second expression */
  448.   if (! this->priv->errors->get_code (this->priv->errors))
  449.     statement->statement.ifn->right = parse_expression ();
  450.  
  451.   /* parse the THEN */
  452.   if (! this->priv->errors->get_code (this->priv->errors)) {
  453.     token = get_token_to_parse ();
  454.     if (token->get_class (token) != TOKEN_THEN)
  455.       this->priv->errors->set_code
  456.         (this->priv->errors, E_THEN_EXPECTED, token->get_line (token),
  457.         this->priv->last_label);
  458.     token->destroy (token);
  459.   }
  460.  
  461.   /* parse the conditional statement */
  462.   if (! this->priv->errors->get_code (this->priv->errors))
  463.     statement->statement.ifn->statement = parse_statement ();
  464.  
  465.   /* destroy the half-made statement if errors occurred */
  466.   if (this->priv->errors->get_code (this->priv->errors)) {
  467.     statement_destroy (statement);
  468.     statement = NULL;
  469.   }
  470.  
  471.   /* return the statement */
  472.   return statement;
  473. }
  474.  
  475. /*
  476.  * Parse a GOTO statement
  477.  * returns:
  478.  *   StatementNode*   the parsed GOTO statement
  479.  */
  480. static StatementNode *parse_goto_statement (void) {
  481.  
  482.   /* local variables */
  483.   StatementNode *statement; /* the IF statement */
  484.  
  485.   /* initialise the statement */
  486.   statement = statement_create ();
  487.   statement->class = STATEMENT_GOTO;
  488.   statement->statement.goton = statement_create_goto ();
  489.  
  490.   /* parse the line label expression */
  491.   if (! (statement->statement.goton->label = parse_expression ())) {
  492.     statement_destroy (statement);
  493.     statement = NULL;
  494.   }
  495.  
  496.   /* return the new statement */
  497.   return statement;
  498. }
  499.  
  500. /*
  501.  * Parse a GOSUB statement
  502.  * returns:
  503.  *   StatementNode*   the parsed GOSUB statement
  504.  */
  505. static StatementNode *parse_gosub_statement (void) {
  506.  
  507.   /* local variables */
  508.   StatementNode *statement; /* the IF statement */
  509.  
  510.   /* initialise the statement */
  511.   statement = statement_create ();
  512.   statement->class = STATEMENT_GOSUB;
  513.   statement->statement.gosubn = statement_create_gosub ();
  514.  
  515.   /* parse the line label expression */
  516.   if (! (statement->statement.gosubn->label = parse_expression ())) {
  517.     statement_destroy (statement);
  518.     statement = NULL;
  519.   }
  520.  
  521.   /* return the new statement */
  522.   return statement;
  523. }
  524.  
  525. /*
  526.  * Parse an RETURN statement
  527.  * returns:
  528.  *   StatementNode*          The statement assembled
  529.  */
  530. static StatementNode *parse_return_statement (void) {
  531.   StatementNode *statement; /* the RETURN */
  532.   statement = statement_create ();
  533.   statement->class = STATEMENT_RETURN;
  534.   return statement;
  535. }
  536.  
  537. /*
  538.  * Parse an END statement
  539.  * returns:
  540.  *   StatementNode*          The statement assembled
  541.  */
  542. static StatementNode *parse_end_statement (void) {
  543.   StatementNode *statement = NULL; /* the END */
  544.   statement = statement_create ();
  545.   statement->class = STATEMENT_END;
  546.   return statement;
  547. }
  548.  
  549. /*
  550.  * Parse a PRINT statement
  551.  * returns:
  552.  *   StatementNode*          The statement assembled
  553.  */
  554. static StatementNode *parse_print_statement (void) {
  555.  
  556.   /* local variables */
  557.   Token *token = NULL; /* tokens read as part of the statement */
  558.   StatementNode *statement; /* the statement we're building */
  559.   int line; /* line containing the PRINT token */
  560.   OutputNode
  561.     *nextoutput = NULL, /* the next output node we're parsing */
  562.     *lastoutput = NULL; /* the last output node we parsed */
  563.   ExpressionNode *expression; /* a parsed expression */
  564.  
  565.   /* initialise the statement */
  566.   statement = statement_create ();
  567.   statement->class = STATEMENT_PRINT;
  568.   statement->statement.printn = statement_create_print ();
  569.   line = this->priv->stream->get_line (this->priv->stream);
  570.  
  571.   /* main loop for parsing the output list */
  572.   do {
  573.  
  574.     /* discard a previous comma, and read the next output value */
  575.     if (token)
  576.       token->destroy (token);
  577.     token = get_token_to_parse ();
  578.  
  579.     /* process a premature end of line */
  580.     if (token->get_class (token) == TOKEN_EOF
  581.       || token->get_class (token) == TOKEN_EOL) {
  582.       this->priv->errors->set_code
  583.         (this->priv->errors, E_INVALID_PRINT_OUTPUT, line,
  584.         this->priv->last_label);
  585.       statement_destroy (statement);
  586.       statement = NULL;
  587.       token->destroy (token);
  588.     }
  589.  
  590.     /* process a literal string */
  591.     else if (token->get_class (token) == TOKEN_STRING) {
  592.       nextoutput = malloc (sizeof (OutputNode));
  593.       nextoutput->class = OUTPUT_STRING;
  594.       nextoutput->output.string = malloc
  595.         (1 + strlen (token->get_content (token)));
  596.       strcpy (nextoutput->output.string, token->get_content (token));
  597.       nextoutput->next = NULL;
  598.       token->destroy (token);
  599.     }
  600.  
  601.     /* attempt to process an expression */
  602.     else {
  603.       this->priv->stored_token = token;
  604.       if ((expression = parse_expression ())) {
  605.         nextoutput = malloc (sizeof (OutputNode));
  606.         nextoutput->class = OUTPUT_EXPRESSION;
  607.         nextoutput->output.expression = expression;
  608.         nextoutput->next = NULL;
  609.       } else {
  610.         this->priv->errors->set_code
  611.           (this->priv->errors, E_INVALID_PRINT_OUTPUT, token->get_line (token),
  612.              this->priv->last_label);
  613.         statement_destroy (statement);
  614.         statement = NULL;
  615.       }
  616.     }
  617.  
  618.     /* add this output item to the statement and look for another */
  619.     if (! this->priv->errors->get_code (this->priv->errors)) {
  620.       if (lastoutput)
  621.         lastoutput->next = nextoutput;
  622.       else
  623.         statement->statement.printn->first = nextoutput;
  624.       lastoutput = nextoutput;
  625.       token = get_token_to_parse ();
  626.     }
  627.  
  628.   /* continue the loop until the statement appears to be finished */
  629.   } while (! this->priv->errors->get_code (this->priv->errors)
  630.     && token->get_class (token) == TOKEN_COMMA);
  631.  
  632.   /* push back the last token and return the assembled statement */
  633.   if (! this->priv->errors->get_code (this->priv->errors))
  634.     this->priv->stored_token = token;
  635.   return statement;
  636. }
  637.  
  638. /*
  639.  * Parse an INPUT statement
  640.  * returns:
  641.  *   StatementNode*          The statement assembled
  642.  */
  643. static StatementNode *parse_input_statement (void) {
  644.  
  645.   /* local variables */
  646.   Token *token = NULL; /* tokens read as part of the statement */
  647.   StatementNode *statement; /* the statement we're building */
  648.   int line; /* line containing the INPUT token */
  649.   VariableListNode
  650.     *nextvar = NULL, /* the next variable node we're parsing */
  651.     *lastvar = NULL; /* the last variable node we parsed */
  652.  
  653.   /* initialise the statement */
  654.   statement = statement_create ();
  655.   statement->class = STATEMENT_INPUT;
  656.   statement->statement.inputn = statement_create_input ();
  657.   line = this->priv->stream->get_line (this->priv->stream);
  658.  
  659.   /* main loop for parsing the variable list */
  660.   do {
  661.  
  662.     /* discard a previous comma, and seek the next variable */
  663.     if (token) token->destroy (token);
  664.     token = get_token_to_parse ();
  665.  
  666.     /* process a premature end of line */
  667.     if (token->get_class (token) == TOKEN_EOF
  668.       || token->get_line (token) != line) {
  669.       this->priv->errors->set_code
  670.         (this->priv->errors, E_INVALID_VARIABLE, line, this->priv->last_label);
  671.       statement_destroy (statement);
  672.       statement = NULL;
  673.     }
  674.  
  675.     /* attempt to process an variable name */
  676.     else if (token->get_class (token) != TOKEN_VARIABLE) {
  677.       this->priv->errors->set_code
  678.         (this->priv->errors, E_INVALID_VARIABLE, token->get_line (token),
  679.         this->priv->last_label);
  680.       statement_destroy (statement);
  681.       statement = NULL;
  682.     } else {
  683.       nextvar = malloc (sizeof (VariableListNode));
  684.       nextvar->variable = *token->get_content (token) & 0x1f;
  685.       nextvar->next = NULL;
  686.       token->destroy (token);
  687.     }
  688.  
  689.     /* add this variable to the statement and look for another */
  690.     if (! this->priv->errors->get_code (this->priv->errors)) {
  691.       if (lastvar)
  692.         lastvar->next = nextvar;
  693.       else
  694.         statement->statement.inputn->first = nextvar;
  695.       lastvar = nextvar;
  696.       token = get_token_to_parse ();
  697.     }
  698.   } while (! this->priv->errors->get_code (this->priv->errors)
  699.     && token->get_class (token) == TOKEN_COMMA);
  700.  
  701.   /* return the assembled statement */
  702.   this->priv->stored_token = token;
  703.   return statement;
  704. }
  705.  
  706. /*
  707.  * Parse a statement from the source file
  708.  * returns:
  709.  *   StatementNode*   a fully-assembled statement, hopefully.
  710.  */
  711. static StatementNode *parse_statement () {
  712.  
  713.   /* local variables */
  714.   Token *token; /* token read */
  715.   StatementNode *statement = NULL; /* the new statement */
  716.  
  717.   /* get the next token */
  718.   token = get_token_to_parse ();
  719.  
  720.   /* check for command */
  721.   switch (token->get_class (token)) {
  722.     case TOKEN_EOL:
  723.       this->priv->stored_token = token;
  724.       statement = NULL;
  725.       break;
  726.     case TOKEN_LET:
  727.       token->destroy (token);
  728.       statement = parse_let_statement ();
  729.       break;
  730.     case TOKEN_IF:
  731.       token->destroy (token);
  732.       statement = parse_if_statement ();
  733.       break;
  734.     case TOKEN_GOTO:
  735.       token->destroy (token);
  736.       statement = parse_goto_statement ();
  737.       break;
  738.     case TOKEN_GOSUB:
  739.       token->destroy (token);
  740.       statement = parse_gosub_statement ();
  741.       break;
  742.     case TOKEN_RETURN:
  743.       token->destroy (token);
  744.       statement = parse_return_statement ();
  745.       break;
  746.     case TOKEN_END:
  747.       token->destroy (token);
  748.       statement = parse_end_statement ();
  749.       break;
  750.     case TOKEN_PRINT:
  751.       token->destroy (token);
  752.       statement = parse_print_statement ();
  753.       break;
  754.     case TOKEN_INPUT:
  755.       token->destroy (token);
  756.       statement = parse_input_statement ();
  757.       break;
  758.     default:
  759.       this->priv->errors->set_code
  760.         (this->priv->errors, E_UNRECOGNISED_COMMAND, token->get_line (token),
  761.         this->priv->last_label);
  762.       token->destroy (token);
  763.   }
  764.  
  765.   /* return the statement */
  766.   return statement;
  767. }
  768.  
  769. /*
  770.  * Parse a line from the source file.
  771.  * returns:
  772.  *   StatementNode           a fully-assembled statement, hopefully.
  773.  */
  774. static ProgramLineNode *parse_program_line (void) {
  775.  
  776.   /* local variables */
  777.   Token *token; /* token read */
  778.   ProgramLineNode *program_line; /* program line read */
  779.   int label_encountered = 0; /* 1 if this line has an explicit label */
  780.  
  781.   /* initialise the program line and get the first token */
  782.   program_line = program_line_create ();
  783.   program_line->label = generate_default_label ();
  784.   token = get_token_to_parse ();
  785.  
  786.   /* deal with end of file */
  787.   if (token->get_class (token) == TOKEN_EOF) {
  788.     token->destroy (token);
  789.     program_line_destroy (program_line);
  790.     return NULL;
  791.   }
  792.  
  793.   /* deal with line label, if supplied */
  794.   if (token->get_class (token) == TOKEN_NUMBER) {
  795.     program_line->label = atoi (token->get_content (token));
  796.     label_encountered = 1;
  797.     token->destroy (token);
  798.   } else
  799.     this->priv->stored_token = token;
  800.  
  801.   /* validate the supplied or implied line label */
  802.   if (! validate_line_label (program_line->label)) {
  803.     this->priv->errors->set_code
  804.       (this->priv->errors, E_INVALID_LINE_NUMBER, this->priv->current_line,
  805.       program_line->label);
  806.     program_line_destroy (program_line);
  807.     return NULL;
  808.   }
  809.   if (label_encountered)
  810.     this->priv->last_label = program_line->label;
  811.  
  812.   /* check for a statement and an EOL */
  813.   program_line->statement = parse_statement ();
  814.   if (! this->priv->errors->get_code (this->priv->errors)) {
  815.     token = get_token_to_parse ();
  816.     if (token->get_class (token) != TOKEN_EOL
  817.       && token->get_class (token) != TOKEN_EOF)
  818.       this->priv->errors->set_code
  819.         (this->priv->errors, E_UNEXPECTED_PARAMETER, this->priv->current_line,
  820.         this->priv->last_label);
  821.     token->destroy (token);
  822.   }
  823.   if (program_line->statement)
  824.     this->priv->last_label = program_line->label;
  825.  
  826.   /* return the program line */
  827.   return program_line;
  828. }
  829.  
  830.  
  831. /*
  832.  * Public Methods
  833.  */
  834.  
  835.  
  836. /*
  837.  * Parse the whole program
  838.  * params:
  839.  *   Parser*   parser   The parser to use
  840.  * returns:
  841.  *   ProgramNode*       The parsed program
  842.  */
  843. static ProgramNode *parse (Parser *parser) {
  844.  
  845.   /* local varables */
  846.   ProgramNode *program; /* the stored program */
  847.   ProgramLineNode
  848.     *previous = NULL, /* the previous line */
  849.     *current; /* the current line */
  850.  
  851.   /* initialise the program */
  852.   this = parser;
  853.   program = malloc (sizeof (ProgramNode));
  854.   program->first = NULL;
  855.  
  856.   /* read lines until reaching an error or end of input */
  857.   while ((current = parse_program_line ())
  858.     && ! this->priv->errors->get_code (this->priv->errors)) {
  859.     if (previous)
  860.       previous->next = current;
  861.     else
  862.       program->first = current;
  863.     previous = current;
  864.   }
  865.  
  866.   /* return the program */
  867.   return program;
  868. }
  869.  
  870. /*
  871.  * Return the current source line we're parsing
  872.  * params:
  873.  *   Parser*   The parser to use
  874.  * returns:
  875.  *   int       the line returned
  876.  */
  877. static int get_line (Parser *parser) {
  878.   return parser->priv->current_line;
  879. }
  880.  
  881. /*
  882.  * Return the label of the source line we're parsing
  883.  * params:
  884.  *   Parser*   parser   The parser to use
  885.  * returns:
  886.  *   int                the label returned
  887.  */
  888. static int get_label (Parser *parser) {
  889.   return parser->priv->last_label;
  890. }
  891.  
  892. /*
  893.  * Destroy this parser object
  894.  * params:
  895.  *   Parser*   parser   the doomed parser
  896.  */
  897. void destroy (Parser *parser) {
  898.   if (parser) {
  899.     if (parser->priv) {
  900.       if (parser->priv->stream)
  901.         parser->priv->stream->destroy (parser->priv->stream);
  902.     }
  903.     free (parser->priv);
  904.   }
  905.   free (parser);
  906. }
  907.  
  908.  
  909. /*
  910.  * Constructors
  911.  */
  912.  
  913.  
  914. /*
  915.  * Constructor
  916.  * params:
  917.  *   ErrorHandler*      errors    the error handler to use
  918.  *   LanguageOptions*   options   the language options to use
  919.  *   FILE*              input     the input file
  920.  * returns:
  921.  *   Parser*                      the new parser
  922.  */
  923. Parser *new_Parser (ErrorHandler *errors, LanguageOptions *options,
  924.   FILE *input) {
  925.  
  926.   /* allocate memory */
  927.   this = malloc (sizeof (Parser));
  928.   this->priv = malloc (sizeof (ParserData));
  929.   this->priv->stream = new_TokenStream (input);
  930.  
  931.   /* initialise methods */
  932.   this->parse = parse;
  933.   this->get_line = get_line;
  934.   this->get_label = get_label;
  935.   this->destroy = destroy;
  936.  
  937.   /* initialise properties */
  938.   this->priv->last_label = 0;
  939.   this->priv->current_line = 0;
  940.   this->priv->end_of_file = 0;
  941.   this->priv->stored_token = NULL;
  942.   this->priv->errors = errors;
  943.   this->priv->options = options;
  944.  
  945.   /* return the new object */
  946.   return this;
  947. }
  948.