Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Tiny BASIC
  3.  * Listing Output Module
  4.  *
  5.  * Released as Public Domain by Damian Gareth Walker, 2019
  6.  * Created: 18-Sep-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 "formatter.h"
  16. #include "expression.h"
  17. #include "errors.h"
  18. #include "parser.h"
  19.  
  20.  
  21. /*
  22.  * Data Definitions
  23.  */
  24.  
  25.  
  26. /* private formatter data */
  27. typedef struct formatter_data {
  28.   ErrorHandler *errors; /* the error handler */
  29. } FormatterData;
  30.  
  31. /* convenience variables */
  32. static Formatter *this; /* the object being worked on */
  33.  
  34. /*
  35.  * Forward References
  36.  */
  37.  
  38.  
  39. /* factor_output() has a forward reference to output_expression() */
  40. static char *output_expression (ExpressionNode *expression);
  41.  
  42. /* output_statement() has a forward reference from output_if() */
  43. static char *output_statement (StatementNode *statement);
  44.  
  45.  
  46. /*
  47.  * Functions
  48.  */
  49.  
  50.  
  51. /*
  52.  * Output a factor
  53.  * params:
  54.  *   FactorNode*   factor   the factor to output
  55.  * return:
  56.  *   char*                  the text representation of the factor
  57.  */
  58. static char *output_factor (FactorNode *factor) {
  59.  
  60.   /* local variables */
  61.   char *factor_text = NULL, /* the text of the whole factor */
  62.     *factor_buffer = NULL, /* temporary buffer for prepending to factor_text */
  63.     *expression_text = NULL; /* the text of a subexpression */
  64.  
  65.   /* work out the main factor text */
  66.   switch (factor->class) {
  67.     case FACTOR_VARIABLE:
  68.       factor_text = malloc (2);
  69.       sprintf (factor_text, "%c", factor->data.variable + 'A' - 1);
  70.       break;
  71.     case FACTOR_VALUE:
  72.       factor_text = malloc (7);
  73.       sprintf (factor_text, "%d", factor->data.value);
  74.       break;
  75.     case FACTOR_EXPRESSION:
  76.       if ((expression_text = output_expression (factor->data.expression))) {
  77.         factor_text = malloc (strlen (expression_text) + 3);
  78.         sprintf (factor_text, "(%s)", expression_text);
  79.         free (expression_text);
  80.       }
  81.       break;
  82.     default:
  83.       this->priv->errors->set_code
  84.         (this->priv->errors, E_INVALID_EXPRESSION, 0, 0);
  85.   }
  86.  
  87.   /* apply a negative sign, if necessary */
  88.   if (factor_text && factor->sign == SIGN_NEGATIVE) {
  89.     factor_buffer = malloc (strlen (factor_text) + 2);
  90.     sprintf (factor_buffer, "-%s", factor_text);
  91.     free (factor_text);
  92.     factor_text = factor_buffer;
  93.   }
  94.  
  95.   /* return the final factor representation */
  96.   return factor_text;
  97. }
  98.  
  99. /*
  100.  * Output a term
  101.  * params:
  102.  *   TermNode*   term   the term to output
  103.  * returns:
  104.  *   char*              the text representation of the term
  105.  */
  106. static char *output_term (TermNode *term) {
  107.  
  108.   /* local variables */
  109.   char
  110.     *term_text = NULL, /* the text of the whole term */
  111.     *factor_text = NULL, /* the text of each factor */
  112.     operator_char; /* the operator that joins the righthand factor */
  113.   RightHandFactor *rhfactor; /* right hand factors of the expression */
  114.  
  115.   /* begin with the initial factor */
  116.   if ((term_text = output_factor (term->factor))) {
  117.     rhfactor = term->next;
  118.     while (! this->priv->errors->get_code (this->priv->errors) && rhfactor) {
  119.  
  120.       /* ascertain the operator text */
  121.       switch (rhfactor->op) {
  122.       case TERM_OPERATOR_MULTIPLY:
  123.         operator_char = '*';
  124.         break;
  125.       case TERM_OPERATOR_DIVIDE:
  126.         operator_char = '/';
  127.         break;
  128.       default:
  129.         this->priv->errors->set_code
  130.           (this->priv->errors, E_INVALID_EXPRESSION, 0, 0);
  131.         free (term_text);
  132.         term_text = NULL;
  133.       }
  134.  
  135.       /* get the factor that follows the operator */
  136.       if (! this->priv->errors->get_code (this->priv->errors)
  137.         && (factor_text = output_factor (rhfactor->factor))) {
  138.         term_text = realloc (term_text,
  139.           strlen (term_text) + strlen (factor_text) + 2);
  140.         sprintf (term_text, "%s%c%s", term_text, operator_char, factor_text);
  141.         free (factor_text);
  142.       }
  143.  
  144.       /* look for another term on the right of the expression */
  145.       rhfactor = rhfactor->next;
  146.     }
  147.   }
  148.  
  149.   /* return the expression text */
  150.   return term_text;
  151.  
  152. }
  153.  
  154. /*
  155.  * Output an expression for a program listing
  156.  * params:
  157.  *   ExpressionNode*   expression   the expression to output
  158.  * returns:
  159.  *   char*                          new string containint the expression text
  160.  */
  161. static char *output_expression (ExpressionNode *expression) {
  162.  
  163.   /* local variables */
  164.   char
  165.     *expression_text = NULL, /* the text of the whole expression */
  166.     *term_text = NULL, /* the text of each term */
  167.     operator_char; /* the operator that joins the righthand term */
  168.   RightHandTerm *rhterm; /* right hand terms of the expression */
  169.  
  170.   /* begin with the initial term */
  171.   if ((expression_text = output_term (expression->term))) {
  172.     rhterm = expression->next;
  173.     while (! this->priv->errors->get_code (this->priv->errors) && rhterm) {
  174.  
  175.       /* ascertain the operator text */
  176.       switch (rhterm->op) {
  177.       case EXPRESSION_OPERATOR_PLUS:
  178.         operator_char = '+';
  179.         break;
  180.       case EXPRESSION_OPERATOR_MINUS:
  181.         operator_char = '-';
  182.         break;
  183.       default:
  184.         this->priv->errors->set_code
  185.           (this->priv->errors, E_INVALID_EXPRESSION, 0, 0);
  186.         free (expression_text);
  187.         expression_text = NULL;
  188.       }
  189.  
  190.       /* get the terms that follow the operators */
  191.       if (! this->priv->errors->get_code (this->priv->errors)
  192.         && (term_text = output_term (rhterm->term))) {
  193.         expression_text = realloc (expression_text,
  194.           strlen (expression_text) + strlen (term_text) + 2);
  195.         sprintf (expression_text, "%s%c%s", expression_text, operator_char,
  196.           term_text);
  197.         free (term_text);
  198.       }
  199.  
  200.       /* look for another term on the right of the expression */
  201.       rhterm = rhterm->next;
  202.     }
  203.   }
  204.  
  205.   /* return the expression text */
  206.   return expression_text;
  207.  
  208. }
  209.  
  210. /*
  211.  * LET statement output
  212.  * params:
  213.  *   LetStatementNode*   letn   data for the LET statement
  214.  * returns:
  215.  *   char*                      the LET statement text
  216.  */
  217. static char *output_let (LetStatementNode *letn) {
  218.  
  219.   /* local variables */
  220.   char
  221.     *let_text = NULL, /* the LET text to be assembled */
  222.     *expression_text = NULL; /* the text of the expression */
  223.  
  224.   /* assemble the expression */
  225.   expression_text = output_expression (letn->expression);
  226.  
  227.   /* assemble the final LET text, if we have an expression */
  228.   if (expression_text) {
  229.     let_text = malloc (7 + strlen (expression_text));
  230.     sprintf (let_text, "LET %c=%s", 'A' - 1 + letn->variable, expression_text);
  231.     free (expression_text);
  232.   }
  233.  
  234.   /* return it */
  235.   return let_text;
  236. }
  237.  
  238.  
  239. /*
  240.  * IF statement output
  241.  * params:
  242.  *   IfStatementNode*   ifn   data for the IF statement
  243.  * returns:
  244.  *   char*                    the IF statement text
  245.  */
  246. static char *output_if (IfStatementNode *ifn) {
  247.  
  248.   /* local variables */
  249.   char
  250.     *if_text = NULL, /* the LET text to be assembled */
  251.     *left_text = NULL, /* the text of the left expression */
  252.     *op_text = NULL, /* the operator text */
  253.     *right_text = NULL, /* the text of the right expression */
  254.     *statement_text = NULL; /* the text of the conditional statement */
  255.  
  256.   /* assemble the expressions and conditional statement */
  257.   left_text = output_expression (ifn->left);
  258.   right_text = output_expression (ifn->right);
  259.   statement_text = output_statement (ifn->statement);
  260.  
  261.   /* work out the operator text */
  262.   op_text = malloc (3);
  263.   switch (ifn->op) {
  264.     case RELOP_EQUAL: strcpy (op_text, "="); break;
  265.     case RELOP_UNEQUAL: strcpy (op_text, "<>"); break;
  266.     case RELOP_LESSTHAN: strcpy (op_text, "<"); break;
  267.     case RELOP_LESSOREQUAL: strcpy (op_text, "<="); break;
  268.     case RELOP_GREATERTHAN: strcpy (op_text, ">"); break;
  269.     case RELOP_GREATEROREQUAL: strcpy (op_text, ">="); break;
  270.   }
  271.  
  272.   /* assemble the final IF text, if we have everything we need */
  273.   if (left_text && op_text && right_text && statement_text) {
  274.     if_text = malloc (3 + strlen (left_text) + strlen (op_text) +
  275.       strlen (right_text) + 6 + strlen (statement_text) + 1);
  276.     sprintf (if_text, "IF %s%s%s THEN %s", left_text, op_text, right_text,
  277.       statement_text);
  278.   }
  279.  
  280.   /* free up the temporary bits of memory we've reserved */
  281.   if (left_text) free (left_text);
  282.   if (op_text) free (op_text);
  283.   if (right_text) free (right_text);
  284.   if (statement_text) free (statement_text);
  285.  
  286.   /* return it */
  287.   return if_text;
  288. }
  289.  
  290.  
  291. /*
  292.  * GOTO statement output
  293.  * params:
  294.  *   GotoStatementNode*   goton   data for the GOTO statement
  295.  * returns:
  296.  *   char*                        the GOTO statement text
  297.  */
  298. static char *output_goto (GotoStatementNode *goton) {
  299.  
  300.   /* local variables */
  301.   char
  302.     *goto_text = NULL, /* the GOTO text to be assembled */
  303.     *expression_text = NULL; /* the text of the expression */
  304.  
  305.   /* assemble the expression */
  306.   expression_text = output_expression (goton->label);
  307.  
  308.   /* assemble the final LET text, if we have an expression */
  309.   if (expression_text) {
  310.     goto_text = malloc (6 + strlen (expression_text));
  311.     sprintf (goto_text, "GOTO %s", expression_text);
  312.     free (expression_text);
  313.   }
  314.  
  315.   /* return it */
  316.   return goto_text;
  317. }
  318.  
  319.  
  320. /*
  321.  * GOSUB statement output
  322.  * params:
  323.  *   GosubStatementNode*   gosubn   data for the GOSUB statement
  324.  * returns:
  325.  *   char*                        the GOSUB statement text
  326.  */
  327. static char *output_gosub (GosubStatementNode *gosubn) {
  328.  
  329.   /* local variables */
  330.   char
  331.     *gosub_text = NULL, /* the GOSUB text to be assembled */
  332.     *expression_text = NULL; /* the text of the expression */
  333.  
  334.   /* assemble the expression */
  335.   expression_text = output_expression (gosubn->label);
  336.  
  337.   /* assemble the final LET text, if we have an expression */
  338.   if (expression_text) {
  339.     gosub_text = malloc (7 + strlen (expression_text));
  340.     sprintf (gosub_text, "GOSUB %s", expression_text);
  341.     free (expression_text);
  342.   }
  343.  
  344.   /* return it */
  345.   return gosub_text;
  346. }
  347.  
  348.  
  349. /*
  350.  * END statement output
  351.  * returns:
  352.  *   char*   A new string with the text "END"
  353.  */
  354. static char *output_end (void) {
  355.   char *end_text; /* the full text of the END command */
  356.   end_text = malloc (4);
  357.   strcpy (end_text, "END");
  358.   return end_text;
  359. }
  360.  
  361.  
  362. /*
  363.  * RETURN statement output
  364.  * returns:
  365.  *   char*   A new string with the text "RETURN"
  366.  */
  367. static char *output_return (void) {
  368.   char *return_text; /* the full text of the RETURN command */
  369.   return_text = malloc (7);
  370.   strcpy (return_text, "RETURN");
  371.   return return_text;
  372. }
  373.  
  374.  
  375. /*
  376.  * PRINT statement output
  377.  * params:
  378.  *   PrintStatementNode*   printn   data for the PRINT statement
  379.  * returns:
  380.  *   char*                          the PRINT statement text
  381.  */
  382. static char *output_print (PrintStatementNode *printn) {
  383.  
  384.   /* local variables */
  385.   char
  386.     *print_text, /* the PRINT text to be assembled */
  387.     *output_text = NULL; /* the text of the current output item */
  388.   OutputNode *output; /* the current output item */
  389.  
  390.   /* initialise the PRINT statement */
  391.   print_text = malloc (6);
  392.   strcpy (print_text, "PRINT");
  393.  
  394.   /* add the output items */
  395.   if ((output = printn->first)) {
  396.     do {
  397.  
  398.       /* add the separator */
  399.       print_text = realloc (print_text, strlen (print_text) + 2);
  400.       strcat (print_text, output == printn->first ? " " : ",");
  401.  
  402.       /* format the output item */
  403.       switch (output->class) {
  404.       case OUTPUT_STRING:
  405.         output_text = malloc (strlen (output->output.string) + 3);
  406.         sprintf (output_text, "%c%s%c", '"', output->output.string, '"');
  407.         break;
  408.       case OUTPUT_EXPRESSION:
  409.         output_text = output_expression (output->output.expression);
  410.         break;
  411.       }
  412.  
  413.       /* add the output item */
  414.       print_text = realloc (print_text,
  415.         strlen (print_text) + strlen (output_text) + 1);
  416.       strcat (print_text, output_text);
  417.       free (output_text);
  418.  
  419.     /* look for the next output item */
  420.     } while ((output = output->next));
  421.   }
  422.  
  423.   /* return the assembled text */
  424.   return print_text;
  425. }
  426.  
  427. /*
  428.  * INPUT statement output
  429.  * params:
  430.  *   InputStatementNode*   inputn   the input statement node to show
  431.  * returns:
  432.  *   char *                         the text of the INPUT statement
  433.  */
  434. static char *output_input (InputStatementNode *inputn) {
  435.  
  436.   /* local variables */
  437.   char
  438.     *input_text, /* the INPUT text to be assembled */
  439.     var_text[3]; /* text representation of each variable with separator */
  440.   VariableListNode *variable; /* the current output item */
  441.  
  442.   /* initialise the INPUT statement */
  443.   input_text = malloc (6);
  444.   strcpy (input_text, "INPUT");
  445.  
  446.   /* add the output items */
  447.   if ((variable = inputn->first)) {
  448.     do {
  449.       sprintf (var_text, "%c%c",
  450.         (variable == inputn->first) ? ' ' : ',',
  451.         variable->variable + 'A' - 1);
  452.       input_text = realloc (input_text, strlen (input_text) + 3);
  453.       strcat (input_text, var_text);
  454.     } while ((variable = variable->next));
  455.   }
  456.  
  457.   /* return the assembled text */
  458.   return input_text;
  459. }
  460.  
  461. /*
  462.  * Statement output
  463.  * params:
  464.  *   StatementNode*   statement   the statement to output
  465.  * returns:
  466.  *   char*                        a string containing the statement line
  467.  */
  468. static char *output_statement (StatementNode *statement) {
  469.  
  470.   /* local variables */
  471.   char *output = NULL; /* the text output */
  472.  
  473.   /* return null output for comments */
  474.   if (! statement)
  475.     return NULL;
  476.  
  477.   /* build the statement itself */
  478.   switch (statement->class) {
  479.     case STATEMENT_LET:
  480.       output = output_let (statement->statement.letn);
  481.       break;
  482.     case STATEMENT_IF:
  483.       output = output_if (statement->statement.ifn);
  484.       break;
  485.     case STATEMENT_GOTO:
  486.       output = output_goto (statement->statement.goton);
  487.       break;
  488.     case STATEMENT_GOSUB:
  489.       output = output_gosub (statement->statement.gosubn);
  490.       break;
  491.     case STATEMENT_RETURN:
  492.       output = output_return ();
  493.       break;
  494.     case STATEMENT_END:
  495.       output = output_end ();
  496.      break;
  497.     case STATEMENT_PRINT:
  498.       output = output_print (statement->statement.printn);
  499.       break;
  500.     case STATEMENT_INPUT:
  501.       output = output_input (statement->statement.inputn);
  502.       break;
  503.     default:
  504.       output = malloc (24);
  505.       strcpy (output, "Unrecognised statement.");
  506.   }
  507.  
  508.   /* return the listing line */
  509.   return output;
  510. }
  511.  
  512. /*
  513.  * Program Line Output
  514.  * params:
  515.  *   ProgramLineNode*   program_line     the line to output
  516.  */
  517. static void generate_line (ProgramLineNode *program_line) {
  518.  
  519.   /* local variables */
  520.   char
  521.     label_text [7], /* line label text */
  522.     *output = NULL, /* the rest of the output */
  523.     *line_text = NULL; /* the assembled line */
  524.  
  525.   /* initialise the line label */
  526.   if (program_line->label)
  527.     sprintf (label_text, "%5d ", program_line->label);
  528.   else
  529.     strcpy (label_text, "      ");
  530.  
  531.   /* build the statement itself */
  532.   output = output_statement (program_line->statement);
  533.  
  534.   /* if this wasn't a comment, add it to the program */
  535.   if (output) {
  536.     line_text = malloc (strlen (label_text) + strlen (output) + 2);
  537.     sprintf (line_text, "%s%s\n", label_text, output);
  538.     free (output);
  539.     this->output = realloc (this->output,
  540.       strlen (this->output) + strlen (line_text) + 1);
  541.     strcat (this->output, line_text);
  542.     free (line_text);
  543.   }
  544. }
  545.  
  546.  
  547. /*
  548.  * Public Methods
  549.  */
  550.  
  551.  
  552. /*
  553.  * Create a formatted version of the program
  554.  * params:
  555.  *   Formatter*     fomatter   the formatter
  556.  *   ProgramNode*   program    the syntax tree
  557.  */
  558. static void generate (Formatter *formatter, ProgramNode *program) {
  559.  
  560.   /* local variables */
  561.   ProgramLineNode *program_line; /* line to process */
  562.  
  563.   /* initialise this object */
  564.   this = formatter;
  565.  
  566.   /* generate the code for the lines */
  567.   program_line = program->first;
  568.   while (program_line) {
  569.     generate_line (program_line);
  570.     program_line = program_line->next;
  571.   }
  572. }
  573.  
  574. /*
  575.  * Destroy the formatter when no longer needed
  576.  * params:
  577.  *   Formatter*   formatter   the doomed formatter
  578.  */
  579. static void destroy (Formatter *formatter) {
  580.   if (formatter) {
  581.     if (formatter->output)
  582.       free (formatter->output);
  583.     if (formatter->priv)
  584.       free (formatter->priv);
  585.     free (formatter);
  586.   }
  587. }
  588.  
  589.  
  590. /*
  591.  * Constructors
  592.  */
  593.  
  594.  
  595. /*
  596.  * The Formatter constructor
  597.  * params:
  598.  *   ErrorHandler   *errors   the error handler object
  599.  * returns:
  600.  *   Formatter*               the new formatter
  601.  */
  602. Formatter *new_Formatter (ErrorHandler *errors) {
  603.  
  604.   /* allocate memory */
  605.   this = malloc (sizeof (Formatter));
  606.   this->priv = malloc (sizeof (FormatterData));
  607.  
  608.   /* initialise methods */
  609.   this->generate = generate;
  610.   this->destroy = destroy;
  611.  
  612.   /* initialise properties */
  613.   this->output = malloc (sizeof (char));
  614.   *this->output = '\0';
  615.   this->priv->errors = errors;
  616.  
  617.   /* return the new object */
  618.   return this;
  619. }
  620.