forked from KolibriOS/kolibrios
(An improved version in conjunction with ktcc can generate executable files.) git-svn-id: svn://kolibrios.org@8733 a494cfbc-eb01-0410-851d-a64ba20cac60
883 lines
24 KiB
C
883 lines
24 KiB
C
/*
|
|
* Tiny BASIC Interpreter and Compiler Project
|
|
* C Output Module
|
|
*
|
|
* Copyright (C) Damian Gareth Walker 2019
|
|
* Created: 03-Oct-2019
|
|
*/
|
|
|
|
|
|
/* included headers */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "statement.h"
|
|
#include "expression.h"
|
|
#include "errors.h"
|
|
#include "parser.h"
|
|
#include "options.h"
|
|
#include "generatec.h"
|
|
|
|
|
|
/*
|
|
* Internal Data
|
|
*/
|
|
|
|
|
|
/* label list */
|
|
typedef struct label {
|
|
int number; /* the label number */
|
|
struct label *next; /* the next label */
|
|
} CLabel;
|
|
|
|
/* private data */
|
|
typedef struct {
|
|
unsigned int input_used:1; /* true if we need the input routine */
|
|
unsigned long int vars_used:26; /* true for each variable used */
|
|
CLabel *first_label; /* the start of a list of labels */
|
|
char *code; /* the main block of generated code */
|
|
ErrorHandler *errors; /* error handler for compilation */
|
|
LanguageOptions *options; /* the language options for compilation */
|
|
} Private;
|
|
|
|
/* convenience variables */
|
|
static CProgram *this; /* the object being worked on */
|
|
static Private *data; /* the private data of the object */
|
|
static ErrorHandler *errors; /* the error handler */
|
|
static LanguageOptions *options; /* the language options */
|
|
|
|
|
|
/*
|
|
* Forward References
|
|
*/
|
|
|
|
|
|
/* factor_output() has a forward reference to output_expression() */
|
|
static char *output_expression (ExpressionNode *expression);
|
|
|
|
/* output_statement() has a forward reference from output_if() */
|
|
static char *output_statement (StatementNode *statement);
|
|
|
|
|
|
/*
|
|
* Level 6 Functions
|
|
*/
|
|
|
|
|
|
/*
|
|
* Output a factor
|
|
* params:
|
|
* FactorNode* factor the factor to output
|
|
* return:
|
|
* char* the text representation of the factor
|
|
*/
|
|
static char *output_factor (FactorNode *factor) {
|
|
|
|
/* local variables */
|
|
char *factor_text = NULL, /* the text of the whole factor */
|
|
*factor_buffer = NULL, /* temporary buffer for prepending to factor_text */
|
|
*expression_text = NULL; /* the text of a subexpression */
|
|
|
|
/* work out the main factor text */
|
|
switch (factor->class) {
|
|
case FACTOR_VARIABLE:
|
|
factor_text = malloc (2);
|
|
sprintf (factor_text, "%c", factor->data.variable + 'a' - 1);
|
|
data->vars_used |= 1 << (factor->data.variable - 1);
|
|
break;
|
|
case FACTOR_VALUE:
|
|
factor_text = malloc (7);
|
|
sprintf (factor_text, "%d", factor->data.value);
|
|
break;
|
|
case FACTOR_EXPRESSION:
|
|
if ((expression_text = output_expression (factor->data.expression))) {
|
|
factor_text = malloc (strlen (expression_text) + 3);
|
|
sprintf (factor_text, "(%s)", expression_text);
|
|
free (expression_text);
|
|
}
|
|
break;
|
|
default:
|
|
errors->set_code (errors, E_INVALID_EXPRESSION, 0, 0);
|
|
}
|
|
|
|
/* apply a negative sign, if necessary */
|
|
if (factor_text && factor->sign == SIGN_NEGATIVE) {
|
|
factor_buffer = malloc (strlen (factor_text) + 2);
|
|
sprintf (factor_buffer, "-%s", factor_text);
|
|
free (factor_text);
|
|
factor_text = factor_buffer;
|
|
}
|
|
|
|
/* return the final factor representation */
|
|
return factor_text;
|
|
}
|
|
|
|
|
|
/*
|
|
* Level 5 Functions
|
|
*/
|
|
|
|
|
|
/*
|
|
* Output a term
|
|
* params:
|
|
* TermNode* term the term to output
|
|
* returns:
|
|
* char* the text representation of the term
|
|
*/
|
|
static char *output_term (TermNode *term) {
|
|
|
|
/* local variables */
|
|
char
|
|
*term_text = NULL, /* the text of the whole term */
|
|
*factor_text = NULL, /* the text of each factor */
|
|
operator_char; /* the operator that joins the righthand factor */
|
|
RightHandFactor *rhfactor; /* right hand factors of the expression */
|
|
|
|
/* begin with the initial factor */
|
|
if ((term_text = output_factor (term->factor))) {
|
|
rhfactor = term->next;
|
|
while (! errors->get_code (errors) && rhfactor) {
|
|
|
|
/* ascertain the operator text */
|
|
switch (rhfactor->op) {
|
|
case TERM_OPERATOR_MULTIPLY:
|
|
operator_char = '*';
|
|
break;
|
|
case TERM_OPERATOR_DIVIDE:
|
|
operator_char = '/';
|
|
break;
|
|
default:
|
|
errors->set_code (errors, E_INVALID_EXPRESSION, 0, 0);
|
|
free (term_text);
|
|
term_text = NULL;
|
|
}
|
|
|
|
/* get the factor that follows the operator */
|
|
if (! errors->get_code (errors)
|
|
&& (factor_text = output_factor (rhfactor->factor))) {
|
|
term_text = realloc (term_text,
|
|
strlen (term_text) + strlen (factor_text) + 2);
|
|
sprintf (term_text, "%s%c%s", term_text, operator_char, factor_text);
|
|
free (factor_text);
|
|
}
|
|
|
|
/* look for another term on the right of the expression */
|
|
rhfactor = rhfactor->next;
|
|
}
|
|
}
|
|
|
|
/* return the expression text */
|
|
return term_text;
|
|
}
|
|
|
|
|
|
/*
|
|
* Level 4 Functions
|
|
*/
|
|
|
|
|
|
/*
|
|
* Output an expression for a program listing
|
|
* params:
|
|
* ExpressionNode* expression the expression to output
|
|
* returns:
|
|
* char* new string containint the expression text
|
|
*/
|
|
static char *output_expression (ExpressionNode *expression) {
|
|
|
|
/* local variables */
|
|
char
|
|
*expression_text = NULL, /* the text of the whole expression */
|
|
*term_text = NULL, /* the text of each term */
|
|
operator_char; /* the operator that joins the righthand term */
|
|
RightHandTerm *rhterm; /* right hand terms of the expression */
|
|
|
|
/* begin with the initial term */
|
|
if ((expression_text = output_term (expression->term))) {
|
|
rhterm = expression->next;
|
|
while (! errors->get_code (errors) && rhterm) {
|
|
|
|
/* ascertain the operator text */
|
|
switch (rhterm->op) {
|
|
case EXPRESSION_OPERATOR_PLUS:
|
|
operator_char = '+';
|
|
break;
|
|
case EXPRESSION_OPERATOR_MINUS:
|
|
operator_char = '-';
|
|
break;
|
|
default:
|
|
errors->set_code (errors, E_INVALID_EXPRESSION, 0, 0);
|
|
free (expression_text);
|
|
expression_text = NULL;
|
|
}
|
|
|
|
/* get the terms that follow the operators */
|
|
if (! errors->get_code (errors)
|
|
&& (term_text = output_term (rhterm->term))) {
|
|
expression_text = realloc (expression_text,
|
|
strlen (expression_text) + strlen (term_text) + 2);
|
|
sprintf (expression_text, "%s%c%s", expression_text, operator_char,
|
|
term_text);
|
|
free (term_text);
|
|
}
|
|
|
|
/* look for another term on the right of the expression */
|
|
rhterm = rhterm->next;
|
|
}
|
|
}
|
|
|
|
/* return the expression text */
|
|
return expression_text;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Level 3 Functions
|
|
*/
|
|
|
|
|
|
/*
|
|
* LET statement output
|
|
* params:
|
|
* LetStatementNode* letn data for the LET statement
|
|
* returns:
|
|
* char* the LET statement text
|
|
*/
|
|
static char *output_let (LetStatementNode *letn) {
|
|
|
|
/* local variables */
|
|
char
|
|
*let_text = NULL, /* the LET text to be assembled */
|
|
*expression_text = NULL; /* the text of the expression */
|
|
|
|
/* assemble the expression */
|
|
expression_text = output_expression (letn->expression);
|
|
|
|
/* assemble the final LET text, if we have an expression */
|
|
if (expression_text) {
|
|
let_text = malloc (4 + strlen (expression_text));
|
|
sprintf (let_text, "%c=%s;", 'a' - 1 + letn->variable, expression_text);
|
|
free (expression_text);
|
|
data->vars_used |= 1 << (letn->variable - 1);
|
|
}
|
|
|
|
/* return it */
|
|
return let_text;
|
|
}
|
|
|
|
/*
|
|
* IF statement output
|
|
* params:
|
|
* IfStatementNode* ifn data for the IF statement
|
|
* returns:
|
|
* char* the IF statement text
|
|
*/
|
|
static char *output_if (IfStatementNode *ifn) {
|
|
|
|
/* local variables */
|
|
char
|
|
*if_text = NULL, /* the LET text to be assembled */
|
|
*left_text = NULL, /* the text of the left expression */
|
|
*op_text = NULL, /* the operator text */
|
|
*right_text = NULL, /* the text of the right expression */
|
|
*statement_text = NULL; /* the text of the conditional statement */
|
|
|
|
/* assemble the expressions and conditional statement */
|
|
left_text = output_expression (ifn->left);
|
|
right_text = output_expression (ifn->right);
|
|
statement_text = output_statement (ifn->statement);
|
|
|
|
/* work out the operator text */
|
|
op_text = malloc (3);
|
|
switch (ifn->op) {
|
|
case RELOP_EQUAL: strcpy (op_text, "=="); break;
|
|
case RELOP_UNEQUAL: strcpy (op_text, "!="); break;
|
|
case RELOP_LESSTHAN: strcpy (op_text, "<"); break;
|
|
case RELOP_LESSOREQUAL: strcpy (op_text, "<="); break;
|
|
case RELOP_GREATERTHAN: strcpy (op_text, ">"); break;
|
|
case RELOP_GREATEROREQUAL: strcpy (op_text, ">="); break;
|
|
}
|
|
|
|
/* assemble the final IF text, if we have everything we need */
|
|
if (left_text && op_text && right_text && statement_text) {
|
|
if_text = malloc (4 + strlen (left_text) + strlen (op_text) +
|
|
strlen (right_text) + 3 + strlen (statement_text) + 2);
|
|
sprintf (if_text, "if (%s%s%s) {%s}", left_text, op_text, right_text,
|
|
statement_text);
|
|
}
|
|
|
|
/* free up the temporary bits of memory we've reserved */
|
|
if (left_text) free (left_text);
|
|
if (op_text) free (op_text);
|
|
if (right_text) free (right_text);
|
|
if (statement_text) free (statement_text);
|
|
|
|
/* return it */
|
|
return if_text;
|
|
}
|
|
|
|
/*
|
|
* GOTO statement output
|
|
* params:
|
|
* GotoStatementNode* goton data for the GOTO statement
|
|
* returns:
|
|
* char* the GOTO statement text
|
|
*/
|
|
static char *output_goto (GotoStatementNode *goton) {
|
|
|
|
/* local variables */
|
|
char
|
|
*goto_text = NULL, /* the GOTO text to be assembled */
|
|
*expression_text = NULL; /* the text of the expression */
|
|
|
|
/* assemble the expression */
|
|
expression_text = output_expression (goton->label);
|
|
|
|
/* assemble the final LET text, if we have an expression */
|
|
if (expression_text) {
|
|
goto_text = malloc (27 + strlen (expression_text));
|
|
sprintf (goto_text, "label=%s; goto goto_block;", expression_text);
|
|
free (expression_text);
|
|
}
|
|
|
|
/* return it */
|
|
return goto_text;
|
|
}
|
|
|
|
/*
|
|
* GOSUB statement output
|
|
* params:
|
|
* GosubStatementNode* gosubn data for the GOSUB statement
|
|
* returns:
|
|
* char* the GOSUB statement text
|
|
*/
|
|
static char *output_gosub (GosubStatementNode *gosubn) {
|
|
|
|
/* local variables */
|
|
char
|
|
*gosub_text = NULL, /* the GOSUB text to be assembled */
|
|
*expression_text = NULL; /* the text of the expression */
|
|
|
|
/* assemble the expression */
|
|
expression_text = output_expression (gosubn->label);
|
|
|
|
/* assemble the final LET text, if we have an expression */
|
|
if (expression_text) {
|
|
gosub_text = malloc (12 + strlen (expression_text));
|
|
sprintf (gosub_text, "bas_exec(%s);", expression_text);
|
|
free (expression_text);
|
|
}
|
|
|
|
/* return it */
|
|
return gosub_text;
|
|
}
|
|
|
|
/*
|
|
* END statement output
|
|
* returns:
|
|
* char* A new string with the text "END"
|
|
*/
|
|
static char *output_end (void) {
|
|
char *end_text; /* the full text of the END command */
|
|
end_text = malloc (9);
|
|
strcpy (end_text, "exit(0);");
|
|
return end_text;
|
|
}
|
|
|
|
/*
|
|
* RETURN statement output
|
|
* returns:
|
|
* char* A new string with the text "RETURN"
|
|
*/
|
|
static char *output_return (void) {
|
|
char *return_text; /* the full text of the RETURN command */
|
|
return_text = malloc (8);
|
|
strcpy (return_text, "return;");
|
|
return return_text;
|
|
}
|
|
|
|
/*
|
|
* PRINT statement output
|
|
* params:
|
|
* PrintStatementNode* printn data for the PRINT statement
|
|
* returns:
|
|
* char* the PRINT statement text
|
|
*/
|
|
static char *output_print (PrintStatementNode *printn) {
|
|
|
|
/* local variables */
|
|
char
|
|
*format_text = NULL, /* the printf format string */
|
|
*output_list = NULL, /* the printf output list */
|
|
*output_text = NULL, /* a single output item */
|
|
*print_text = NULL; /* the PRINT text to be assembled */
|
|
OutputNode *output; /* the current output item */
|
|
|
|
/* initialise format and output text */
|
|
format_text = malloc (1);
|
|
*format_text = '\0';
|
|
output_list = malloc (1);
|
|
*output_list = '\0';
|
|
|
|
/* build the format and output text */
|
|
if ((output = printn->first)) {
|
|
do {
|
|
|
|
/* format the output item */
|
|
switch (output->class) {
|
|
case OUTPUT_STRING:
|
|
format_text = realloc (format_text,
|
|
strlen (format_text) + strlen (output->output.string) + 1);
|
|
strcat (format_text, output->output.string);
|
|
break;
|
|
case OUTPUT_EXPRESSION:
|
|
format_text = realloc (format_text, strlen (format_text) + 3);
|
|
strcat (format_text, "%d");
|
|
output_text = output_expression (output->output.expression);
|
|
output_list = realloc (output_list,
|
|
strlen (output_list) + 1 + strlen (output_text) + 1);
|
|
strcat (output_list, ",");
|
|
strcat (output_list, output_text);
|
|
free (output_text);
|
|
break;
|
|
}
|
|
|
|
/* look for the next output item */
|
|
} while ((output = output->next));
|
|
}
|
|
|
|
/* assemble the whole print text and return it */
|
|
print_text = malloc (8 + strlen (format_text) + 3 + strlen (output_list) + 3);
|
|
sprintf (print_text, "printf(\"%s\\n\"%s);", format_text, output_list);
|
|
free (format_text);
|
|
free (output_list);
|
|
return print_text;
|
|
}
|
|
|
|
/*
|
|
* INPUT statement output
|
|
* params:
|
|
* InputStatementNode* inputn the input statement node to show
|
|
* returns:
|
|
* char * the text of the INPUT statement
|
|
*/
|
|
static char *output_input (InputStatementNode *inputn) {
|
|
|
|
/* local variables */
|
|
char
|
|
*var_text, /* input text for a single variable */
|
|
*input_text; /* the INPUT text to be assembled */
|
|
VariableListNode *variable; /* the current output item */
|
|
|
|
/* generate an input line for each variable listed */
|
|
input_text = malloc (1);
|
|
*input_text = '\0';
|
|
if ((variable = inputn->first)) {
|
|
do {
|
|
var_text = malloc (18);
|
|
sprintf (var_text, "%s%c = bas_input();",
|
|
(variable == inputn->first) ? "" : "\n",
|
|
variable->variable + 'a' - 1);
|
|
input_text = realloc (input_text,
|
|
strlen (input_text) + strlen (var_text) + 1);
|
|
strcat (input_text, var_text);
|
|
free (var_text);
|
|
data->vars_used |= 1 << (variable->variable - 1);
|
|
} while ((variable = variable->next));
|
|
}
|
|
data->input_used = 1;
|
|
|
|
/* return the assembled text */
|
|
return input_text;
|
|
}
|
|
|
|
|
|
/*
|
|
* Level 2 Functions
|
|
*/
|
|
|
|
|
|
/*
|
|
* Statement output
|
|
* params:
|
|
* StatementNode* statement the statement to output
|
|
* returns:
|
|
* char* a string containing the statement line
|
|
*/
|
|
static char *output_statement (StatementNode *statement) {
|
|
|
|
/* local variables */
|
|
char *output = NULL; /* the text output */
|
|
|
|
/* return null output for comments */
|
|
if (! statement)
|
|
return NULL;
|
|
|
|
/* build the statement itself */
|
|
switch (statement->class) {
|
|
case STATEMENT_LET:
|
|
output = output_let (statement->statement.letn);
|
|
break;
|
|
case STATEMENT_IF:
|
|
output = output_if (statement->statement.ifn);
|
|
break;
|
|
case STATEMENT_GOTO:
|
|
output = output_goto (statement->statement.goton);
|
|
break;
|
|
case STATEMENT_GOSUB:
|
|
output = output_gosub (statement->statement.gosubn);
|
|
break;
|
|
case STATEMENT_RETURN:
|
|
output = output_return ();
|
|
break;
|
|
case STATEMENT_END:
|
|
output = output_end ();
|
|
break;
|
|
case STATEMENT_PRINT:
|
|
output = output_print (statement->statement.printn);
|
|
break;
|
|
case STATEMENT_INPUT:
|
|
output = output_input (statement->statement.inputn);
|
|
break;
|
|
default:
|
|
output = malloc (24);
|
|
strcpy (output, "Unrecognised statement.");
|
|
}
|
|
|
|
/* return the listing line */
|
|
return output;
|
|
}
|
|
|
|
|
|
/*
|
|
* Level 1 Functions
|
|
*/
|
|
|
|
|
|
/*
|
|
* Program Line Generation
|
|
* params:
|
|
* ProgramLineNode* program_line the program line to convert
|
|
*/
|
|
static void generate_line (ProgramLineNode *program_line) {
|
|
|
|
/* local variables */
|
|
CLabel
|
|
*prior_label, /* label before potential insertion point */
|
|
*next_label, /* label after potential insertion point */
|
|
*new_label; /* a label to insert */
|
|
char
|
|
label_text[12], /* text of a line label */
|
|
*statement_text; /* the text of a statement */
|
|
|
|
/* generate a line label */
|
|
if (program_line->label) {
|
|
|
|
/* insert the label into the label list */
|
|
new_label = malloc (sizeof (CLabel));
|
|
new_label->number = program_line->label;
|
|
new_label->next = NULL;
|
|
prior_label = NULL;
|
|
next_label = data->first_label;
|
|
while (next_label && next_label->number < new_label->number) {
|
|
prior_label = next_label;
|
|
next_label = prior_label->next;
|
|
}
|
|
new_label->next = next_label;
|
|
if (prior_label)
|
|
prior_label->next = new_label;
|
|
else
|
|
data->first_label = new_label;
|
|
|
|
/* append the label to the code block */
|
|
sprintf (label_text, "lbl_%d:\n", program_line->label);
|
|
data->code = realloc (data->code,
|
|
strlen (data->code) + strlen (label_text) + 1);
|
|
strcat (data->code, label_text);
|
|
}
|
|
|
|
/* generate the statement, and append it if it is not a comment */
|
|
statement_text = output_statement (program_line->statement);
|
|
if (statement_text) {
|
|
data->code = realloc (data->code,
|
|
strlen (data->code) + strlen (statement_text) + 2);
|
|
strcat (data->code, statement_text);
|
|
strcat (data->code, "\n");
|
|
free (statement_text);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Generate the #include lines and #defines
|
|
* changes:
|
|
* Private* data appends headers to the output
|
|
*/
|
|
static void generate_includes (void) {
|
|
|
|
/* local variables */
|
|
char
|
|
include_text[1024], /* the whole include and #define text */
|
|
define_text[80]; /* a single #define line */
|
|
|
|
/* build up includes and defines */
|
|
strcpy (include_text, "#include <stdio.h>\n");
|
|
strcat (include_text, "#include <stdlib.h>\n");
|
|
sprintf (define_text, "#define E_RETURN_WITHOUT_GOSUB %d\n",
|
|
E_RETURN_WITHOUT_GOSUB);
|
|
strcat (include_text, define_text);
|
|
|
|
/* add the #includes and #defines to the output */
|
|
this->c_output = realloc (this->c_output, strlen (this->c_output)
|
|
+ strlen (include_text) + 1);
|
|
strcat (this->c_output, include_text);
|
|
}
|
|
|
|
/*
|
|
* Generate the variable declarations
|
|
* changes:
|
|
* Private* data appends declaration to the output
|
|
*/
|
|
static void generate_variables (void) {
|
|
|
|
/* local variables */
|
|
int vcount; /* variable counter */
|
|
char
|
|
var_text [12], /* individual variable text */
|
|
declaration[60]; /* declaration text */
|
|
|
|
/* build the declaration */
|
|
*declaration = '\0';
|
|
for (vcount = 0; vcount < 26; ++vcount) {
|
|
if (data->vars_used & 1 << vcount) {
|
|
if (*declaration)
|
|
sprintf (var_text, ",%c", 'a' + vcount);
|
|
else
|
|
sprintf (var_text, "short int %c", 'a' + vcount);
|
|
strcat (declaration, var_text);
|
|
}
|
|
}
|
|
|
|
/* if there are any variables, add the declaration to the output */
|
|
if (*declaration) {
|
|
strcat (declaration, ";\n");
|
|
this->c_output = realloc (this->c_output, strlen (this->c_output)
|
|
+ strlen (declaration) + 1);
|
|
strcat (this->c_output, declaration);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Generate the bas_input function
|
|
* changes:
|
|
* Private* data appends declaration to the output
|
|
*/
|
|
static void generate_bas_input (void) {
|
|
|
|
/* local variables */
|
|
char function_text[1024]; /* the entire function */
|
|
|
|
/* construct the function text */
|
|
strcpy (function_text, "short int bas_input (void) {\n");
|
|
strcat (function_text, "short int ch, sign, value;\n");
|
|
strcat (function_text, "do {\n");
|
|
strcat (function_text, "if (ch == '-') sign = -1; else sign = 1;\n");
|
|
strcat (function_text, "ch = getchar ();\n");
|
|
strcat (function_text, "} while (ch < '0' || ch > '9');\n");
|
|
strcat (function_text, "value = 0;\n");
|
|
strcat (function_text, "do {\n");
|
|
strcat (function_text, "value = 10 * value + (ch - '0');\n");
|
|
strcat (function_text, "ch = getchar ();\n");
|
|
strcat (function_text, "} while (ch >= '0' && ch <= '9');\n");
|
|
strcat (function_text, "return sign * value;\n");
|
|
strcat (function_text, "}\n");
|
|
|
|
/* add the function text to the output */
|
|
this->c_output = realloc (this->c_output, strlen (this->c_output)
|
|
+ strlen (function_text) + 1);
|
|
strcat (this->c_output, function_text);
|
|
}
|
|
|
|
/*
|
|
* Generate the bas_exec function
|
|
* changes:
|
|
* Private* data appends declaration to the output
|
|
*/
|
|
static void generate_bas_exec (void) {
|
|
|
|
/* local variables */
|
|
char
|
|
*op, /* comparison operator to use for line numbers */
|
|
goto_line[80], /* a line in the goto block */
|
|
*goto_block, /* the goto block */
|
|
*function_text; /* the complete function text */
|
|
CLabel *label; /* label pointer for construction goto block */
|
|
|
|
/* decide which operator to use for comparison */
|
|
op = (options->get_line_numbers (options) == LINE_NUMBERS_OPTIONAL)
|
|
? "=="
|
|
: "<=";
|
|
|
|
/* create the goto block */
|
|
goto_block = malloc (128);
|
|
strcpy (goto_block, "goto_block:\n");
|
|
strcat (goto_block, "if (!label) goto lbl_start;\n");
|
|
label = data->first_label;
|
|
while (label) {
|
|
sprintf (goto_line, "if (label%s%d) goto lbl_%d;\n",
|
|
op, label->number, label->number);
|
|
goto_block = realloc (goto_block,
|
|
strlen (goto_block) + strlen (goto_line) + 1);
|
|
strcat (goto_block, goto_line);
|
|
label = label->next;
|
|
}
|
|
goto_block = realloc (goto_block, strlen (goto_block) + 12);
|
|
strcat (goto_block, "lbl_start:\n");
|
|
|
|
/* put the function together */
|
|
function_text = malloc (28 + strlen (goto_block) + strlen (data->code) + 3);
|
|
strcpy (function_text, "void bas_exec (int label) {\n");
|
|
strcat (function_text, goto_block);
|
|
strcat (function_text, data->code);
|
|
strcat (function_text, "}\n");
|
|
|
|
/* add the function text to the output */
|
|
this->c_output = realloc (this->c_output, strlen (this->c_output)
|
|
+ strlen (function_text) + 1);
|
|
strcat (this->c_output, function_text);
|
|
free (goto_block);
|
|
free (function_text);
|
|
}
|
|
|
|
/*
|
|
* Generate the main function
|
|
* changes:
|
|
* Private* data appends declaration to the output
|
|
*/
|
|
void generate_main (void) {
|
|
|
|
/* local variables */
|
|
char function_text[1024]; /* the entire function */
|
|
|
|
/* construct the function text */
|
|
strcpy (function_text, "int main (void) {\n");
|
|
strcat (function_text, "bas_exec (0);\n");
|
|
strcat (function_text, "exit (E_RETURN_WITHOUT_GOSUB);\n");
|
|
strcat (function_text, "}\n");
|
|
|
|
/* add the function text to the output */
|
|
this->c_output = realloc (this->c_output, strlen (this->c_output)
|
|
+ strlen (function_text) + 1);
|
|
strcat (this->c_output, function_text);
|
|
}
|
|
|
|
|
|
/*
|
|
* Top Level Functions
|
|
*/
|
|
|
|
|
|
/*
|
|
* Program Generation
|
|
* params:
|
|
* ProgramNode* program the program parse tree to convert to C
|
|
* returns:
|
|
* char* the program output
|
|
*/
|
|
static void generate (CProgram *c_program, ProgramNode *program) {
|
|
|
|
/* local variables */
|
|
ProgramLineNode *program_line; /* line to process */
|
|
|
|
/* initialise this object */
|
|
this = c_program;
|
|
data = (Private *) c_program->private_data;
|
|
|
|
/* generate the code for the lines */
|
|
program_line = program->first;
|
|
while (program_line) {
|
|
generate_line (program_line);
|
|
program_line = program_line->next;
|
|
}
|
|
|
|
/* put the code together */
|
|
generate_includes ();
|
|
generate_variables ();
|
|
if (data->input_used)
|
|
generate_bas_input ();
|
|
generate_bas_exec ();
|
|
generate_main ();
|
|
}
|
|
|
|
/*
|
|
* Destructor
|
|
* params:
|
|
* CProgram* c_program The C program to destroy
|
|
*/
|
|
static void destroy (CProgram *c_program) {
|
|
|
|
/* local variables */
|
|
CLabel
|
|
*current_label, /* pointer to label to destroy */
|
|
*next_label; /* pointer to next label to destroy */
|
|
|
|
/* destroy the private data */
|
|
this = c_program;
|
|
if (this->private_data) {
|
|
data = (Private *) c_program->private_data;
|
|
next_label = data->first_label;
|
|
while (next_label) {
|
|
current_label = next_label;
|
|
next_label = current_label->next;
|
|
free (current_label);
|
|
}
|
|
if (data->code)
|
|
free (data->code);
|
|
free (data);
|
|
}
|
|
|
|
/* destroy the generated output */
|
|
if (this->c_output)
|
|
free (this->c_output);
|
|
|
|
/* destroy the containing structure */
|
|
free (c_program);
|
|
}
|
|
|
|
/*
|
|
* Constructor
|
|
* params:
|
|
* ErrorHandler* compiler_errors the error handler
|
|
* changes:
|
|
* CProgram* this the object being created
|
|
* Private* data the object's private data
|
|
* returns:
|
|
* CProgram* the created object
|
|
*/
|
|
CProgram *new_CProgram (ErrorHandler *compiler_errors,
|
|
LanguageOptions *compiler_options) {
|
|
|
|
/* allocate space */
|
|
this = malloc (sizeof (CProgram));
|
|
this->private_data = data = malloc (sizeof (Private));
|
|
|
|
/* initialise methods */
|
|
this->generate = generate;
|
|
this->destroy = destroy;
|
|
|
|
/* initialise properties */
|
|
errors = data->errors = compiler_errors;
|
|
options = data->options = compiler_options;
|
|
data->input_used = 0;
|
|
data->vars_used = 0;
|
|
data->first_label = NULL;
|
|
data->code = malloc (1);
|
|
*data->code = '\0';
|
|
this->c_output = malloc (1);
|
|
*this->c_output = '\0';
|
|
|
|
/* return the created structure */
|
|
return this;
|
|
}
|