/*
* Tiny BASIC
* Interpreter and Compiler Main Program
*
* Released as Public Domain by Damian Gareth Walker 2019
* Created: 04-Aug-2019
*/
/* included headers */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "options.h"
#include "errors.h"
#include "parser.h"
#include "statement.h"
#include "interpret.h"
#include "formatter.h"
#include "generatec.h"
#ifdef _KOLIBRI
#define KTCC_BIN "/kolibrios/develop/tcc/tcc"
#define KTCC_FLAGS "%s -o %s -lck"
#endif
/* static variables */
static char *input_filename = NULL; /* name of the input file */
static enum { /* action to take with parsed program */
OUTPUT_INTERPRET, /* interpret the program */
OUTPUT_LST, /* output a formatted listing */
OUTPUT_C, /* output a C program */
OUTPUT_EXE /* output an executable */
} output = OUTPUT_INTERPRET;
static ErrorHandler *errors; /* universal error handler */
static LanguageOptions *loptions; /* language options */
/*
* Level 2 Routines
*/
/*
* Set line number option
* params:
* char* option the option supplied on the command line
*/
static void set_line_numbers (char *option) {
loptions->set_line_numbers (loptions, LINE_NUMBERS_OPTIONAL);
loptions->set_line_numbers (loptions, LINE_NUMBERS_IMPLIED);
loptions->set_line_numbers (loptions, LINE_NUMBERS_MANDATORY);
else
errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
}
/*
* Set line number limit
* params:
* char* option the option supplied on the command line
*/
static void set_line_limit (char *option) {
int limit; /* the limit contained in the option */
if (sscanf (option
, "%d", &limit
))
loptions->set_line_limit (loptions, limit);
else
errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
}
/*
* Set comment option
* params:
* char* option the option supplied on the command line
*/
static void set_comments (char *option) {
loptions->set_comments (loptions, COMMENTS_ENABLED);
loptions->set_comments (loptions, COMMENTS_DISABLED);
else
errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
}
/*
* Set the output options
* params:
* char* option the option supplied on the command line
*/
static void set_output (char *option) {
output = OUTPUT_LST;
else if (! strcmp ("c", option
))
output = OUTPUT_C;
else if (! strcmp ("exe", option
))
output = OUTPUT_EXE;
else
errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
}
/*
* Set the GOSUB stack limit option
* params:
* char* option the option supplied on the command line
*/
static void set_gosub_limit (char *option) {
int limit; /* the limit contained in the option */
if (sscanf (option
, "%d", &limit
))
loptions->set_gosub_limit (loptions, limit);
else
errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
}
/*
* Level 1 Routines
*/
/*
* Process the command line options
* params:
* int argc number of arguments on the command line
* char** argv the arguments
*/
static void set_options (int argc, char **argv) {
/* local variables */
int argn; /* argument number count */
/* loop through all parameters */
for (argn = 1; argn < argc && ! errors->get_code (errors); ++argn) {
/* scan for line number options */
if (! strncmp (argv
[argn
], "-n", 2))
set_line_numbers (&argv[argn][2]);
else if (! strncmp (argv
[argn
], "--line-numbers=", 15))
set_line_numbers (&argv[argn][15]);
/* scan for line number limit */
else if (! strncmp (argv
[argn
], "-N", 2))
set_line_limit (&argv[argn][2]);
else if (! strncmp (argv
[argn
], "--line-limit=", 13))
set_line_limit (&argv[argn][13]);
/* scan for comment option */
else if (! strncmp (argv
[argn
], "-o", 2))
set_comments (&argv[argn][2]);
else if (! strncmp (argv
[argn
], "--comments=", 11))
set_comments (&argv[argn][11]);
/* scan for output option */
else if (! strncmp (argv
[argn
], "-O", 2))
set_output (&argv[argn][2]);
else if (! strncmp (argv
[argn
], "--output=", 9))
set_output (&argv[argn][9]);
/* scan for gosub stack limit */
else if (! strncmp (argv
[argn
], "-g", 2))
set_gosub_limit (&argv[argn][2]);
else if (! strncmp (argv
[argn
], "--gosub-limit=", 14))
set_gosub_limit (&argv[argn][14]);
/* accept filename */
else if (! input_filename)
input_filename = argv[argn];
/* raise an error upon illegal option */
else
errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
}
}
/*
* Output a formatted program listing
* params:
* ProgramNode* program the program to output
*/
static void output_lst (ProgramNode *program) {
/* local variables */
FILE *output; /* the output file */
char *output_filename; /* the output filename */
Formatter *formatter; /* the formatter object */
/* ascertain the output filename */
if (output_filename) {
/* open the output file */
sprintf (output_filename
, "%s.lst", input_filename
);
if ((output
= fopen (output_filename
, "w"))) {
/* write to the output file */
formatter = new_Formatter (errors);
if (formatter) {
formatter->generate (formatter, program);
if (formatter->output)
fprintf (output
, "%s", formatter
->output
);
formatter->destroy (formatter);
}
}
/* deal with errors */
else
errors->set_code (errors, E_FILE_NOT_FOUND, 0, 0);
/* free the output filename */
}
/* deal with out of memory error */
else
errors->set_code (errors, E_MEMORY, 0, 0);
}
/*
* Output a C source file
* params:
* ProgramNode* program the parsed program
*/
static void output_c (ProgramNode *program) {
/* local variables */
FILE *output; /* the output file */
char *output_filename; /* the output filename */
CProgram *c_program; /* the C program */
/* open the output file */
sprintf (output_filename
, "%s.c", input_filename
);
if ((output
= fopen (output_filename
, "w"))) {
/* write to the output file */
c_program = new_CProgram (errors, loptions);
if (c_program) {
c_program->generate (c_program, program);
if (c_program->c_output)
fprintf (output
, "%s", c_program
->c_output
);
c_program->destroy (c_program);
}
}
/* deal with errors */
else
errors->set_code (errors, E_FILE_NOT_FOUND, 0, 0);
/* clean up allocated memory */
}
/*
* Invoke a compiler to turn a C source file into an executable
* params:
* char* basic_filename The BASIC program's name
*/
static void output_exe (char *command, char *basic_filename) {
/* local variables */
char
c_filename[256], /* the name of the C source */
exe_filename[256], /* the base name of the executable */
final_command[1024], /* the constructed compiler command */
*ext, /* position of extension character '.' in filename */
*src, /* source pointer for string copying */
*dst; /* destination pointer for string copying */
/* work out the C and EXE filenames */
sprintf (c_filename
, "%s.c", basic_filename
);
strcpy (exe_filename
, basic_filename
);
if ((ext
= strchr (exe_filename
, '.')))
*ext = '\0';
else
strcat (exe_filename
, ".out");
#ifndef _KOLIBRI
/* build the compiler command */
src = command;
dst = final_command;
while (*src) {
} else
*(dst++) = *(src++);
}
*dst = '\0';
/* run the compiler command */
#else
sprintf(final_command
, KTCC_FLAGS
, c_filename
, exe_filename
);
if(!_ksys_exec(KTCC_BIN, final_command)){
}else{
printf("Bad command: %s %s\n", KTCC_BIN
, final_command
);
}
#endif
}
/*
* Top Level Routine
*/
/*
* Main Program
* params:
* int argc number of arguments on the command line
* char** argv the arguments
* returns:
* int any error code from processing/running the program
*/
int main (int argc, char **argv) {
/* local variables */
FILE *input; /* input file */
ProgramNode *program; /* the parsed program */
ErrorCode code; /* error returned */
Parser *parser; /* parser object */
Interpreter *interpreter; /* interpreter object */
char
*error_text, /* error text message */
*command; /* command for compilation */
/* interpret the command line arguments */
errors = new_ErrorHandler ();
loptions = new_LanguageOptions ();
set_options (argc, argv);
/* give usage if filename not given */
if (! input_filename) {
printf ("Usage: tinybas [OPTIONS] INPUT-FILE\n");
errors->destroy (errors);
loptions->destroy (loptions);
return 0;
}
/* otherwise attempt to open the file */
if (!(input
= fopen (input_filename
, "r"))) {
printf ("Error: cannot open file %s\n", input_filename
);
errors->destroy (errors);
loptions->destroy (loptions);
return E_FILE_NOT_FOUND;
}
/* get the parse tree */
parser = new_Parser (errors, loptions, input);
program = parser->parse (parser);
parser->destroy (parser);
/* deal with errors */
if ((code = errors->get_code (errors))) {
error_text = errors->get_text (errors);
printf ("Parse error: %s\n", error_text
);
loptions->destroy (loptions);
errors->destroy (errors);
return code;
}
/* perform the desired action */
switch (output) {
case OUTPUT_INTERPRET:
interpreter = new_Interpreter (errors, loptions);
interpreter->interpret (interpreter, program);
interpreter->destroy (interpreter);
if ((code = errors->get_code (errors))) {
error_text = errors->get_text (errors);
printf ("Runtime error: %s\n", error_text
);
}
break;
case OUTPUT_LST:
output_lst (program);
break;
case OUTPUT_C:
output_c (program);
break;
case OUTPUT_EXE:
#ifndef _KOLIBRI
if ((command
= getenv ("TBEXE"))) {
output_c (program);
output_exe (command, input_filename);
} else {
break;
}
#else
output_c (program);
output_exe (NULL, input_filename);
break;
#endif
}
/* clean up and return success */
program_destroy (program);
loptions->destroy (loptions);
errors->destroy (errors);
}