Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Tiny BASIC Interpreter and Compiler Project
  3.  * C Output Module
  4.  *
  5.  * Copyright (C) Damian Gareth Walker 2019
  6.  * Created: 03-Oct-2019
  7.  */
  8.  
  9.  
  10. /* included headers */
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include "statement.h"
  15. #include "expression.h"
  16. #include "errors.h"
  17. #include "parser.h"
  18. #include "options.h"
  19. #include "generatec.h"
  20.  
  21.  
  22. /*
  23.  * Internal Data
  24.  */
  25.  
  26.  
  27. /* label list */
  28. typedef struct label {
  29.   int number; /* the label number */
  30.   struct label *next; /* the next label */
  31. } CLabel;
  32.  
  33. /* private data */
  34. typedef struct {
  35.   unsigned int input_used:1; /* true if we need the input routine */
  36.   unsigned long int vars_used:26; /* true for each variable used */
  37.   CLabel *first_label; /* the start of a list of labels */
  38.   char *code; /* the main block of generated code */
  39.   ErrorHandler *errors; /* error handler for compilation */
  40.   LanguageOptions *options; /* the language options for compilation */
  41. } Private;
  42.  
  43. /* convenience variables */
  44. static CProgram *this; /* the object being worked on */
  45. static Private *data; /* the private data of the object */
  46. static ErrorHandler *errors; /* the error handler */
  47. static LanguageOptions *options; /* the language options */
  48.  
  49.  
  50. /*
  51.  * Forward References
  52.  */
  53.  
  54.  
  55. /* factor_output() has a forward reference to output_expression() */
  56. static char *output_expression (ExpressionNode *expression);
  57.  
  58. /* output_statement() has a forward reference from output_if() */
  59. static char *output_statement (StatementNode *statement);
  60.  
  61.  
  62. /*
  63.  * Level 6 Functions
  64.  */
  65.  
  66.  
  67. /*
  68.  * Output a factor
  69.  * params:
  70.  *   FactorNode*   factor   the factor to output
  71.  * return:
  72.  *   char*                  the text representation of the factor
  73.  */
  74. static char *output_factor (FactorNode *factor) {
  75.  
  76.   /* local variables */
  77.   char *factor_text = NULL, /* the text of the whole factor */
  78.     *factor_buffer = NULL, /* temporary buffer for prepending to factor_text */
  79.     *expression_text = NULL; /* the text of a subexpression */
  80.  
  81.   /* work out the main factor text */
  82.   switch (factor->class) {
  83.     case FACTOR_VARIABLE:
  84.       factor_text = malloc (2);
  85.       sprintf (factor_text, "%c", factor->data.variable + 'a' - 1);
  86.       data->vars_used |= 1 << (factor->data.variable - 1);
  87.       break;
  88.     case FACTOR_VALUE:
  89.       factor_text = malloc (7);
  90.       sprintf (factor_text, "%d", factor->data.value);
  91.       break;
  92.     case FACTOR_EXPRESSION:
  93.       if ((expression_text = output_expression (factor->data.expression))) {
  94.         factor_text = malloc (strlen (expression_text) + 3);
  95.         sprintf (factor_text, "(%s)", expression_text);
  96.         free (expression_text);
  97.       }
  98.       break;
  99.     default:
  100.       errors->set_code (errors, E_INVALID_EXPRESSION, 0, 0);
  101.   }
  102.  
  103.   /* apply a negative sign, if necessary */
  104.   if (factor_text && factor->sign == SIGN_NEGATIVE) {
  105.     factor_buffer = malloc (strlen (factor_text) + 2);
  106.     sprintf (factor_buffer, "-%s", factor_text);
  107.     free (factor_text);
  108.     factor_text = factor_buffer;
  109.   }
  110.  
  111.   /* return the final factor representation */
  112.   return factor_text;
  113. }
  114.  
  115.  
  116. /*
  117.  * Level 5 Functions
  118.  */
  119.  
  120.  
  121. /*
  122.  * Output a term
  123.  * params:
  124.  *   TermNode*   term   the term to output
  125.  * returns:
  126.  *   char*              the text representation of the term
  127.  */
  128. static char *output_term (TermNode *term) {
  129.  
  130.   /* local variables */
  131.   char
  132.     *term_text = NULL, /* the text of the whole term */
  133.     *factor_text = NULL, /* the text of each factor */
  134.     operator_char; /* the operator that joins the righthand factor */
  135.   RightHandFactor *rhfactor; /* right hand factors of the expression */
  136.  
  137.   /* begin with the initial factor */
  138.   if ((term_text = output_factor (term->factor))) {
  139.     rhfactor = term->next;
  140.     while (! errors->get_code (errors) && rhfactor) {
  141.  
  142.       /* ascertain the operator text */
  143.       switch (rhfactor->op) {
  144.       case TERM_OPERATOR_MULTIPLY:
  145.         operator_char = '*';
  146.         break;
  147.       case TERM_OPERATOR_DIVIDE:
  148.         operator_char = '/';
  149.         break;
  150.       default:
  151.         errors->set_code (errors, E_INVALID_EXPRESSION, 0, 0);
  152.         free (term_text);
  153.         term_text = NULL;
  154.       }
  155.  
  156.       /* get the factor that follows the operator */
  157.       if (! errors->get_code (errors)
  158.         && (factor_text = output_factor (rhfactor->factor))) {
  159.         term_text = realloc (term_text,
  160.           strlen (term_text) + strlen (factor_text) + 2);
  161.         sprintf (term_text, "%s%c%s", term_text, operator_char, factor_text);
  162.         free (factor_text);
  163.       }
  164.  
  165.       /* look for another term on the right of the expression */
  166.       rhfactor = rhfactor->next;
  167.     }
  168.   }
  169.  
  170.   /* return the expression text */
  171.   return term_text;
  172. }
  173.  
  174.  
  175. /*
  176.  * Level 4 Functions
  177.  */
  178.  
  179.  
  180. /*
  181.  * Output an expression for a program listing
  182.  * params:
  183.  *   ExpressionNode*   expression   the expression to output
  184.  * returns:
  185.  *   char*                          new string containint the expression text
  186.  */
  187. static char *output_expression (ExpressionNode *expression) {
  188.  
  189.   /* local variables */
  190.   char
  191.     *expression_text = NULL, /* the text of the whole expression */
  192.     *term_text = NULL, /* the text of each term */
  193.     operator_char; /* the operator that joins the righthand term */
  194.   RightHandTerm *rhterm; /* right hand terms of the expression */
  195.  
  196.   /* begin with the initial term */
  197.   if ((expression_text = output_term (expression->term))) {
  198.     rhterm = expression->next;
  199.     while (! errors->get_code (errors) && rhterm) {
  200.  
  201.       /* ascertain the operator text */
  202.       switch (rhterm->op) {
  203.       case EXPRESSION_OPERATOR_PLUS:
  204.         operator_char = '+';
  205.         break;
  206.       case EXPRESSION_OPERATOR_MINUS:
  207.         operator_char = '-';
  208.         break;
  209.       default:
  210.         errors->set_code (errors, E_INVALID_EXPRESSION, 0, 0);
  211.         free (expression_text);
  212.         expression_text = NULL;
  213.       }
  214.  
  215.       /* get the terms that follow the operators */
  216.       if (! errors->get_code (errors)
  217.         && (term_text = output_term (rhterm->term))) {
  218.         expression_text = realloc (expression_text,
  219.           strlen (expression_text) + strlen (term_text) + 2);
  220.         sprintf (expression_text, "%s%c%s", expression_text, operator_char,
  221.           term_text);
  222.         free (term_text);
  223.       }
  224.  
  225.       /* look for another term on the right of the expression */
  226.       rhterm = rhterm->next;
  227.     }
  228.   }
  229.  
  230.   /* return the expression text */
  231.   return expression_text;
  232.  
  233. }
  234.  
  235.  
  236. /*
  237.  * Level 3 Functions
  238.  */
  239.  
  240.  
  241. /*
  242.  * LET statement output
  243.  * params:
  244.  *   LetStatementNode*   letn   data for the LET statement
  245.  * returns:
  246.  *   char*                      the LET statement text
  247.  */
  248. static char *output_let (LetStatementNode *letn) {
  249.  
  250.   /* local variables */
  251.   char
  252.     *let_text = NULL, /* the LET text to be assembled */
  253.     *expression_text = NULL; /* the text of the expression */
  254.  
  255.   /* assemble the expression */
  256.   expression_text = output_expression (letn->expression);
  257.  
  258.   /* assemble the final LET text, if we have an expression */
  259.   if (expression_text) {
  260.     let_text = malloc (4 + strlen (expression_text));
  261.     sprintf (let_text, "%c=%s;", 'a' - 1 + letn->variable, expression_text);
  262.     free (expression_text);
  263.     data->vars_used |= 1 << (letn->variable - 1);
  264.   }
  265.  
  266.   /* return it */
  267.   return let_text;
  268. }
  269.  
  270. /*
  271.  * IF statement output
  272.  * params:
  273.  *   IfStatementNode*   ifn   data for the IF statement
  274.  * returns:
  275.  *   char*                    the IF statement text
  276.  */
  277. static char *output_if (IfStatementNode *ifn) {
  278.  
  279.   /* local variables */
  280.   char
  281.     *if_text = NULL, /* the LET text to be assembled */
  282.     *left_text = NULL, /* the text of the left expression */
  283.     *op_text = NULL, /* the operator text */
  284.     *right_text = NULL, /* the text of the right expression */
  285.     *statement_text = NULL; /* the text of the conditional statement */
  286.  
  287.   /* assemble the expressions and conditional statement */
  288.   left_text = output_expression (ifn->left);
  289.   right_text = output_expression (ifn->right);
  290.   statement_text = output_statement (ifn->statement);
  291.  
  292.   /* work out the operator text */
  293.   op_text = malloc (3);
  294.   switch (ifn->op) {
  295.     case RELOP_EQUAL: strcpy (op_text, "=="); break;
  296.     case RELOP_UNEQUAL: strcpy (op_text, "!="); break;
  297.     case RELOP_LESSTHAN: strcpy (op_text, "<"); break;
  298.     case RELOP_LESSOREQUAL: strcpy (op_text, "<="); break;
  299.     case RELOP_GREATERTHAN: strcpy (op_text, ">"); break;
  300.     case RELOP_GREATEROREQUAL: strcpy (op_text, ">="); break;
  301.   }
  302.  
  303.   /* assemble the final IF text, if we have everything we need */
  304.   if (left_text && op_text && right_text && statement_text) {
  305.     if_text = malloc (4 + strlen (left_text) + strlen (op_text) +
  306.       strlen (right_text) + 3 + strlen (statement_text) + 2);
  307.     sprintf (if_text, "if (%s%s%s) {%s}", left_text, op_text, right_text,
  308.       statement_text);
  309.   }
  310.  
  311.   /* free up the temporary bits of memory we've reserved */
  312.   if (left_text) free (left_text);
  313.   if (op_text) free (op_text);
  314.   if (right_text) free (right_text);
  315.   if (statement_text) free (statement_text);
  316.  
  317.   /* return it */
  318.   return if_text;
  319. }
  320.  
  321. /*
  322.  * GOTO statement output
  323.  * params:
  324.  *   GotoStatementNode*   goton   data for the GOTO statement
  325.  * returns:
  326.  *   char*                        the GOTO statement text
  327.  */
  328. static char *output_goto (GotoStatementNode *goton) {
  329.  
  330.   /* local variables */
  331.   char
  332.     *goto_text = NULL, /* the GOTO text to be assembled */
  333.     *expression_text = NULL; /* the text of the expression */
  334.  
  335.   /* assemble the expression */
  336.   expression_text = output_expression (goton->label);
  337.  
  338.   /* assemble the final LET text, if we have an expression */
  339.   if (expression_text) {
  340.     goto_text = malloc (27 + strlen (expression_text));
  341.     sprintf (goto_text, "label=%s; goto goto_block;", expression_text);
  342.     free (expression_text);
  343.   }
  344.  
  345.   /* return it */
  346.   return goto_text;
  347. }
  348.  
  349. /*
  350.  * GOSUB statement output
  351.  * params:
  352.  *   GosubStatementNode*   gosubn   data for the GOSUB statement
  353.  * returns:
  354.  *   char*                        the GOSUB statement text
  355.  */
  356. static char *output_gosub (GosubStatementNode *gosubn) {
  357.  
  358.   /* local variables */
  359.   char
  360.     *gosub_text = NULL, /* the GOSUB text to be assembled */
  361.     *expression_text = NULL; /* the text of the expression */
  362.  
  363.   /* assemble the expression */
  364.   expression_text = output_expression (gosubn->label);
  365.  
  366.   /* assemble the final LET text, if we have an expression */
  367.   if (expression_text) {
  368.     gosub_text = malloc (12 + strlen (expression_text));
  369.     sprintf (gosub_text, "bas_exec(%s);", expression_text);
  370.     free (expression_text);
  371.   }
  372.  
  373.   /* return it */
  374.   return gosub_text;
  375. }
  376.  
  377. /*
  378.  * END statement output
  379.  * returns:
  380.  *   char*   A new string with the text "END"
  381.  */
  382. static char *output_end (void) {
  383.   char *end_text; /* the full text of the END command */
  384.   end_text = malloc (9);
  385.   strcpy (end_text, "exit(0);");
  386.   return end_text;
  387. }
  388.  
  389. /*
  390.  * RETURN statement output
  391.  * returns:
  392.  *   char*   A new string with the text "RETURN"
  393.  */
  394. static char *output_return (void) {
  395.   char *return_text; /* the full text of the RETURN command */
  396.   return_text = malloc (8);
  397.   strcpy (return_text, "return;");
  398.   return return_text;
  399. }
  400.  
  401. /*
  402.  * PRINT statement output
  403.  * params:
  404.  *   PrintStatementNode*   printn   data for the PRINT statement
  405.  * returns:
  406.  *   char*                          the PRINT statement text
  407.  */
  408. static char *output_print (PrintStatementNode *printn) {
  409.  
  410.   /* local variables */
  411.   char
  412.     *format_text = NULL, /* the printf format string */
  413.     *output_list = NULL, /* the printf output list */
  414.     *output_text = NULL, /* a single output item */
  415.     *print_text = NULL; /* the PRINT text to be assembled */
  416.   OutputNode *output; /* the current output item */
  417.  
  418.   /* initialise format and output text */
  419.   format_text = malloc (1);
  420.   *format_text = '\0';
  421.   output_list = malloc (1);
  422.   *output_list = '\0';
  423.  
  424.   /* build the format and output text */
  425.   if ((output = printn->first)) {
  426.     do {
  427.  
  428.       /* format the output item */
  429.       switch (output->class) {
  430.         case OUTPUT_STRING:
  431.           format_text = realloc (format_text,
  432.             strlen (format_text) + strlen (output->output.string) + 1);
  433.           strcat (format_text, output->output.string);
  434.           break;
  435.         case OUTPUT_EXPRESSION:
  436.           format_text = realloc (format_text, strlen (format_text) + 3);
  437.           strcat (format_text, "%d");
  438.           output_text = output_expression (output->output.expression);
  439.           output_list = realloc (output_list,
  440.             strlen (output_list) + 1 + strlen (output_text) + 1);
  441.           strcat (output_list, ",");
  442.           strcat (output_list, output_text);
  443.           free (output_text);
  444.           break;
  445.       }
  446.  
  447.     /* look for the next output item */
  448.     } while ((output = output->next));
  449.   }
  450.  
  451.   /* assemble the whole print text and return it */
  452.   print_text = malloc (8 + strlen (format_text) + 3 + strlen (output_list) + 3);
  453.   sprintf (print_text, "printf(\"%s\\n\"%s);", format_text, output_list);
  454.   free (format_text);
  455.   free (output_list);
  456.   return print_text;
  457. }
  458.  
  459. /*
  460.  * INPUT statement output
  461.  * params:
  462.  *   InputStatementNode*   inputn   the input statement node to show
  463.  * returns:
  464.  *   char *                         the text of the INPUT statement
  465.  */
  466. static char *output_input (InputStatementNode *inputn) {
  467.  
  468.   /* local variables */
  469.   char
  470.     *var_text, /* input text for a single variable */
  471.     *input_text; /* the INPUT text to be assembled */
  472.   VariableListNode *variable; /* the current output item */
  473.  
  474.   /* generate an input line for each variable listed */
  475.   input_text = malloc (1);
  476.   *input_text = '\0';
  477.   if ((variable = inputn->first)) {
  478.     do {
  479.       var_text = malloc (18);
  480.       sprintf (var_text, "%s%c = bas_input();",
  481.         (variable == inputn->first) ? "" : "\n",
  482.         variable->variable + 'a' - 1);
  483.       input_text = realloc (input_text,
  484.         strlen (input_text) + strlen (var_text) + 1);
  485.       strcat (input_text, var_text);
  486.       free (var_text);
  487.       data->vars_used |= 1 << (variable->variable - 1);
  488.     } while ((variable = variable->next));
  489.   }
  490.   data->input_used = 1;
  491.  
  492.   /* return the assembled text */
  493.   return input_text;
  494. }
  495.  
  496.  
  497. /*
  498.  * Level 2 Functions
  499.  */
  500.  
  501.  
  502. /*
  503.  * Statement output
  504.  * params:
  505.  *   StatementNode*   statement   the statement to output
  506.  * returns:
  507.  *   char*                        a string containing the statement line
  508.  */
  509. static char *output_statement (StatementNode *statement) {
  510.  
  511.   /* local variables */
  512.   char *output = NULL; /* the text output */
  513.  
  514.   /* return null output for comments */
  515.   if (! statement)
  516.     return NULL;
  517.  
  518.   /* build the statement itself */
  519.   switch (statement->class) {
  520.     case STATEMENT_LET:
  521.       output = output_let (statement->statement.letn);
  522.       break;
  523.     case STATEMENT_IF:
  524.       output = output_if (statement->statement.ifn);
  525.       break;
  526.     case STATEMENT_GOTO:
  527.       output = output_goto (statement->statement.goton);
  528.       break;
  529.     case STATEMENT_GOSUB:
  530.       output = output_gosub (statement->statement.gosubn);
  531.       break;
  532.     case STATEMENT_RETURN:
  533.       output = output_return ();
  534.       break;
  535.     case STATEMENT_END:
  536.      output = output_end ();
  537.      break;
  538.     case STATEMENT_PRINT:
  539.       output = output_print (statement->statement.printn);
  540.       break;
  541.     case STATEMENT_INPUT:
  542.       output = output_input (statement->statement.inputn);
  543.       break;
  544.     default:
  545.       output = malloc (24);
  546.       strcpy (output, "Unrecognised statement.");
  547.   }
  548.  
  549.   /* return the listing line */
  550.   return output;
  551. }
  552.  
  553.  
  554. /*
  555.  * Level 1 Functions
  556.  */
  557.  
  558.  
  559. /*
  560.  * Program Line Generation
  561.  * params:
  562.  *   ProgramLineNode*   program_line   the program line to convert
  563.  */
  564. static void generate_line (ProgramLineNode *program_line) {
  565.  
  566.   /* local variables */
  567.   CLabel
  568.     *prior_label, /* label before potential insertion point */
  569.     *next_label, /* label after potential insertion point */
  570.     *new_label; /* a label to insert */
  571.   char
  572.     label_text[12], /* text of a line label */
  573.     *statement_text; /* the text of a statement */
  574.  
  575.   /* generate a line label */
  576.   if (program_line->label) {
  577.  
  578.     /* insert the label into the label list */
  579.     new_label = malloc (sizeof (CLabel));
  580.     new_label->number = program_line->label;
  581.     new_label->next = NULL;
  582.     prior_label = NULL;
  583.     next_label = data->first_label;
  584.     while (next_label && next_label->number < new_label->number) {
  585.       prior_label = next_label;
  586.       next_label = prior_label->next;
  587.     }
  588.     new_label->next = next_label;
  589.     if (prior_label)
  590.       prior_label->next = new_label;
  591.     else
  592.       data->first_label = new_label;
  593.  
  594.     /* append the label to the code block */
  595.     sprintf (label_text, "lbl_%d:\n", program_line->label);
  596.     data->code = realloc (data->code,
  597.       strlen (data->code) + strlen (label_text) + 1);
  598.     strcat (data->code, label_text);
  599.   }
  600.  
  601.   /* generate the statement, and append it if it is not a comment */
  602.   statement_text = output_statement (program_line->statement);
  603.   if (statement_text) {
  604.     data->code = realloc (data->code,
  605.       strlen (data->code) + strlen (statement_text) + 2);
  606.     strcat (data->code, statement_text);
  607.     strcat (data->code, "\n");
  608.     free (statement_text);
  609.   }
  610. }
  611.  
  612. /*
  613.  * Generate the #include lines and #defines
  614.  * changes:
  615.  *   Private*   data   appends headers to the output
  616.  */
  617. static void generate_includes (void) {
  618.  
  619.   /* local variables */
  620.   char
  621.     include_text[1024], /* the whole include and #define text */
  622.     define_text[80]; /* a single #define line */
  623.  
  624.   /* build up includes and defines */
  625.   strcpy (include_text, "#include <stdio.h>\n");
  626.   strcat (include_text, "#include <stdlib.h>\n");
  627.   sprintf (define_text, "#define E_RETURN_WITHOUT_GOSUB %d\n",
  628.     E_RETURN_WITHOUT_GOSUB);
  629.   strcat (include_text, define_text);
  630.  
  631.   /* add the #includes and #defines to the output */
  632.   this->c_output = realloc (this->c_output, strlen (this->c_output)
  633.     + strlen (include_text) + 1);
  634.   strcat (this->c_output, include_text);
  635. }
  636.  
  637. /*
  638.  * Generate the variable declarations
  639.  * changes:
  640.  *   Private*   data   appends declaration to the output
  641.  */
  642. static void generate_variables (void) {
  643.  
  644.   /* local variables */
  645.   int vcount; /* variable counter */
  646.   char
  647.     var_text [12], /* individual variable text */
  648.     declaration[60]; /* declaration text */
  649.  
  650.   /* build the declaration */
  651.   *declaration = '\0';
  652.   for (vcount = 0; vcount < 26; ++vcount) {
  653.     if (data->vars_used & 1 << vcount) {
  654.       if (*declaration)
  655.         sprintf (var_text, ",%c", 'a' + vcount);
  656.       else
  657.         sprintf (var_text, "short int %c", 'a' + vcount);
  658.       strcat (declaration, var_text);
  659.     }
  660.   }
  661.  
  662.   /* if there are any variables, add the declaration to the output */
  663.   if (*declaration) {
  664.     strcat (declaration, ";\n");
  665.     this->c_output = realloc (this->c_output, strlen (this->c_output)
  666.       + strlen (declaration) + 1);
  667.     strcat (this->c_output, declaration);
  668.   }
  669. }
  670.  
  671. /*
  672.  * Generate the bas_input function
  673.  * changes:
  674.  *   Private*   data   appends declaration to the output
  675.  */
  676. static void generate_bas_input (void) {
  677.  
  678.   /* local variables */
  679.   char function_text[1024]; /* the entire function */
  680.  
  681.   /* construct the function text */
  682.   strcpy (function_text, "short int bas_input (void) {\n");
  683.   strcat (function_text, "short int ch, sign, value;\n");
  684.   strcat (function_text, "do {\n");
  685.   strcat (function_text, "if (ch == '-') sign = -1; else sign = 1;\n");
  686.   strcat (function_text, "ch = getchar ();\n");
  687.   strcat (function_text, "} while (ch < '0' || ch > '9');\n");
  688.   strcat (function_text, "value = 0;\n");
  689.   strcat (function_text, "do {\n");
  690.   strcat (function_text, "value = 10 * value + (ch - '0');\n");
  691.   strcat (function_text, "ch = getchar ();\n");
  692.   strcat (function_text, "} while (ch >= '0' && ch <= '9');\n");
  693.   strcat (function_text, "return sign * value;\n");
  694.   strcat (function_text, "}\n");
  695.  
  696.   /* add the function text to the output */
  697.   this->c_output = realloc (this->c_output, strlen (this->c_output)
  698.     + strlen (function_text) + 1);
  699.   strcat (this->c_output, function_text);
  700. }
  701.  
  702. /*
  703.  * Generate the bas_exec function
  704.  * changes:
  705.  *   Private*   data   appends declaration to the output
  706.  */
  707. static void generate_bas_exec (void) {
  708.  
  709.   /* local variables */
  710.   char
  711.     *op, /* comparison operator to use for line numbers */
  712.     goto_line[80], /* a line in the goto block */
  713.     *goto_block, /* the goto block */
  714.     *function_text; /* the complete function text */
  715.   CLabel *label; /* label pointer for construction goto block */
  716.  
  717.   /* decide which operator to use for comparison */
  718.   op = (options->get_line_numbers (options) == LINE_NUMBERS_OPTIONAL)
  719.     ? "=="
  720.     : "<=";
  721.  
  722.   /* create the goto block */
  723.   goto_block = malloc (128);
  724.   strcpy (goto_block, "goto_block:\n");
  725.   strcat (goto_block, "if (!label) goto lbl_start;\n");
  726.   label = data->first_label;
  727.   while (label) {
  728.     sprintf (goto_line, "if (label%s%d) goto lbl_%d;\n",
  729.       op, label->number, label->number);
  730.     goto_block = realloc (goto_block,
  731.       strlen (goto_block) + strlen (goto_line) + 1);
  732.     strcat (goto_block, goto_line);
  733.     label = label->next;
  734.   }
  735.   goto_block = realloc (goto_block, strlen (goto_block) + 12);
  736.   strcat (goto_block, "lbl_start:\n");
  737.  
  738.   /* put the function together */
  739.   function_text = malloc (28 + strlen (goto_block) + strlen (data->code) + 3);
  740.   strcpy (function_text, "void bas_exec (int label) {\n");
  741.   strcat (function_text, goto_block);
  742.   strcat (function_text, data->code);
  743.   strcat (function_text, "}\n");
  744.  
  745.   /* add the function text to the output */
  746.   this->c_output = realloc (this->c_output, strlen (this->c_output)
  747.     + strlen (function_text) + 1);
  748.   strcat (this->c_output, function_text);
  749.   free (goto_block);
  750.   free (function_text);
  751. }
  752.  
  753. /*
  754.  * Generate the main function
  755.  * changes:
  756.  *   Private*   data   appends declaration to the output
  757.  */
  758. void generate_main (void) {
  759.  
  760.   /* local variables */
  761.   char function_text[1024]; /* the entire function */
  762.  
  763.   /* construct the function text */
  764.   strcpy (function_text, "int main (void) {\n");
  765.   strcat (function_text, "bas_exec (0);\n");
  766.   strcat (function_text, "exit (E_RETURN_WITHOUT_GOSUB);\n");
  767.   strcat (function_text, "}\n");
  768.  
  769.   /* add the function text to the output */
  770.   this->c_output = realloc (this->c_output, strlen (this->c_output)
  771.     + strlen (function_text) + 1);
  772.   strcat (this->c_output, function_text);
  773. }
  774.  
  775.  
  776. /*
  777.  * Top Level Functions
  778.  */
  779.  
  780.  
  781. /*
  782.  * Program Generation
  783.  * params:
  784.  *   ProgramNode*   program   the program parse tree to convert to C
  785.  * returns:
  786.  *   char*                    the program output
  787.  */
  788. static void generate (CProgram *c_program, ProgramNode *program) {
  789.  
  790.   /* local variables */
  791.   ProgramLineNode *program_line; /* line to process */
  792.  
  793.   /* initialise this object */
  794.   this = c_program;
  795.   data = (Private *) c_program->private_data;
  796.  
  797.   /* generate the code for the lines */
  798.   program_line = program->first;
  799.   while (program_line) {
  800.     generate_line (program_line);
  801.     program_line = program_line->next;
  802.   }
  803.  
  804.   /* put the code together */
  805.   generate_includes ();
  806.   generate_variables ();
  807.   if (data->input_used)
  808.     generate_bas_input ();
  809.   generate_bas_exec ();
  810.   generate_main ();
  811. }
  812.  
  813. /*
  814.  * Destructor
  815.  * params:
  816.  *   CProgram*   c_program   The C program to destroy
  817.  */
  818. static void destroy (CProgram *c_program) {
  819.  
  820.   /* local variables */
  821.   CLabel
  822.     *current_label, /* pointer to label to destroy */
  823.     *next_label; /* pointer to next label to destroy */
  824.  
  825.   /* destroy the private data */
  826.   this = c_program;
  827.   if (this->private_data) {
  828.     data = (Private *) c_program->private_data;
  829.     next_label = data->first_label;
  830.     while (next_label) {
  831.       current_label = next_label;
  832.       next_label = current_label->next;
  833.       free (current_label);
  834.     }
  835.     if (data->code)
  836.       free (data->code);
  837.     free (data);
  838.   }
  839.  
  840.   /* destroy the generated output */
  841.   if (this->c_output)
  842.     free (this->c_output);
  843.  
  844.   /* destroy the containing structure */
  845.   free (c_program);
  846. }
  847.  
  848. /*
  849.  * Constructor
  850.  * params:
  851.  *   ErrorHandler*   compiler_errors   the error handler
  852.  * changes:
  853.  *   CProgram*       this              the object being created
  854.  *   Private*        data              the object's private data
  855.  * returns:
  856.  *   CProgram*                         the created object
  857.  */
  858. CProgram *new_CProgram (ErrorHandler *compiler_errors,
  859.   LanguageOptions *compiler_options) {
  860.  
  861.   /* allocate space */
  862.   this = malloc (sizeof (CProgram));
  863.   this->private_data = data = malloc (sizeof (Private));
  864.  
  865.   /* initialise methods */
  866.   this->generate = generate;
  867.   this->destroy = destroy;
  868.  
  869.   /* initialise properties */
  870.   errors = data->errors = compiler_errors;
  871.   options = data->options = compiler_options;
  872.   data->input_used = 0;
  873.   data->vars_used = 0;
  874.   data->first_label = NULL;
  875.   data->code = malloc (1);
  876.   *data->code = '\0';
  877.   this->c_output = malloc (1);
  878.   *this->c_output = '\0';
  879.  
  880.   /* return the created structure */
  881.   return this;
  882. }
  883.