Subversion Repositories Kolibri OS

Rev

Rev 9234 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Tiny BASIC
  3.  * Interpreter and Compiler Main Program
  4.  *
  5.  * Released as Public Domain by Damian Gareth Walker 2019
  6.  * Created: 04-Aug-2019
  7.  */
  8.  
  9.  
  10. /* included headers */
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include "options.h"
  15. #include "errors.h"
  16. #include "parser.h"
  17. #include "statement.h"
  18. #include "interpret.h"
  19. #include "formatter.h"
  20. #include "generatec.h"
  21.  
  22. #ifdef _KOLIBRI
  23.     #include <sys/ksys.h>
  24.     #define KTCC_BIN "/kolibrios/develop/tcc/tcc"
  25.     #define KTCC_FLAGS "-nobss %s -o %s"
  26. #endif
  27.  
  28. /* static variables */
  29. static char *input_filename = NULL; /* name of the input file */
  30. static enum { /* action to take with parsed program */
  31.   OUTPUT_INTERPRET, /* interpret the program */
  32.   OUTPUT_LST, /* output a formatted listing */
  33.   OUTPUT_C, /* output a C program */
  34.   OUTPUT_EXE /* output an executable */
  35. } output = OUTPUT_INTERPRET;
  36. static ErrorHandler *errors; /* universal error handler */
  37. static LanguageOptions *loptions; /* language options */
  38.  
  39.  
  40. /*
  41.  * Level 2 Routines
  42.  */
  43.  
  44.  
  45. /*
  46.  * Set line number option
  47.  * params:
  48.  *   char*   option   the option supplied on the command line
  49.  */
  50. static void set_line_numbers (char *option) {
  51.   if (! strncmp ("optional", option, strlen (option)))
  52.     loptions->set_line_numbers (loptions, LINE_NUMBERS_OPTIONAL);
  53.   else if (! strncmp ("implied", option, strlen (option)))
  54.     loptions->set_line_numbers (loptions, LINE_NUMBERS_IMPLIED);
  55.   else if (! strncmp ("mandatory", option, strlen (option)))
  56.     loptions->set_line_numbers (loptions, LINE_NUMBERS_MANDATORY);
  57.   else
  58.     errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
  59. }
  60.  
  61. /*
  62.  * Set line number limit
  63.  * params:
  64.  *   char*   option   the option supplied on the command line
  65.  */
  66. static void set_line_limit (char *option) {
  67.   int limit; /* the limit contained in the option */
  68.   if (sscanf (option, "%d", &limit))
  69.     loptions->set_line_limit (loptions, limit);
  70.   else
  71.     errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
  72. }
  73.  
  74. /*
  75.  * Set comment option
  76.  * params:
  77.  *   char*   option   the option supplied on the command line
  78.  */
  79. static void set_comments (char *option) {
  80.   if (! strncmp ("enabled", option, strlen (option)))
  81.     loptions->set_comments (loptions, COMMENTS_ENABLED);
  82.   else if (! strncmp ("disabled", option, strlen (option)))
  83.     loptions->set_comments (loptions, COMMENTS_DISABLED);
  84.   else
  85.     errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
  86. }
  87.  
  88. /*
  89.  * Set the output options
  90.  * params:
  91.  *   char*   option   the option supplied on the command line
  92.  */
  93. static void set_output (char *option) {
  94.   if (! strcmp ("lst", option))
  95.     output = OUTPUT_LST;
  96.   else if (! strcmp ("c", option))
  97.     output = OUTPUT_C;
  98.   else if (! strcmp ("exe", option))
  99.     output = OUTPUT_EXE;
  100.   else
  101.     errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
  102. }
  103.  
  104. /*
  105.  * Set the GOSUB stack limit option
  106.  * params:
  107.  *   char*   option   the option supplied on the command line
  108.  */
  109. static void set_gosub_limit (char *option) {
  110.   int limit; /* the limit contained in the option */
  111.   if (sscanf (option, "%d", &limit))
  112.     loptions->set_gosub_limit (loptions, limit);
  113.   else
  114.     errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
  115. }
  116.  
  117.  
  118. /*
  119.  * Level 1 Routines
  120.  */
  121.  
  122.  
  123. /*
  124.  * Process the command line options
  125.  * params:
  126.  *   int     argc   number of arguments on the command line
  127.  *   char**  argv   the arguments
  128.  */
  129. static void set_options (int argc, char **argv) {
  130.  
  131.   /* local variables */
  132.   int argn; /* argument number count */
  133.  
  134.   /* loop through all parameters */
  135.   for (argn = 1; argn < argc && ! errors->get_code (errors); ++argn) {
  136.  
  137.     /* scan for line number options */
  138.     if (! strncmp (argv[argn], "-n", 2))
  139.       set_line_numbers (&argv[argn][2]);
  140.     else if (! strncmp (argv[argn], "--line-numbers=", 15))
  141.       set_line_numbers (&argv[argn][15]);
  142.  
  143.     /* scan for line number limit */
  144.     else if (! strncmp (argv[argn], "-N", 2))
  145.       set_line_limit (&argv[argn][2]);
  146.     else if (! strncmp (argv[argn], "--line-limit=", 13))
  147.       set_line_limit (&argv[argn][13]);
  148.  
  149.     /* scan for comment option */
  150.     else if (! strncmp (argv[argn], "-o", 2))
  151.       set_comments (&argv[argn][2]);
  152.     else if (! strncmp (argv[argn], "--comments=", 11))
  153.       set_comments (&argv[argn][11]);
  154.  
  155.     /* scan for output option */
  156.     else if (! strncmp (argv[argn], "-O", 2))
  157.       set_output (&argv[argn][2]);
  158.     else if (! strncmp (argv[argn], "--output=", 9))
  159.       set_output (&argv[argn][9]);
  160.  
  161.     /* scan for gosub stack limit */
  162.     else if (! strncmp (argv[argn], "-g", 2))
  163.       set_gosub_limit (&argv[argn][2]);
  164.     else if (! strncmp (argv[argn], "--gosub-limit=", 14))
  165.       set_gosub_limit (&argv[argn][14]);
  166.  
  167.     /* accept filename */
  168.     else if (! input_filename)
  169.       input_filename = argv[argn];
  170.  
  171.     /* raise an error upon illegal option */
  172.     else
  173.       errors->set_code (errors, E_BAD_COMMAND_LINE, 0, 0);
  174.   }
  175. }
  176.  
  177. /*
  178.  * Output a formatted program listing
  179.  * params:
  180.  *   ProgramNode*   program   the program to output
  181.  */
  182. static void output_lst (ProgramNode *program) {
  183.  
  184.   /* local variables */
  185.   FILE *output; /* the output file */
  186.   char *output_filename; /* the output filename */
  187.   Formatter *formatter; /* the formatter object */
  188.  
  189.   /* ascertain the output filename */
  190.   output_filename = malloc (strlen (input_filename) + 5);
  191.   if (output_filename) {
  192.  
  193.     /* open the output file */
  194.     sprintf (output_filename, "%s.lst", input_filename);
  195.     if ((output = fopen (output_filename, "w"))) {
  196.  
  197.       /* write to the output file */
  198.       formatter = new_Formatter (errors);
  199.       if (formatter) {
  200.         formatter->generate (formatter, program);
  201.         if (formatter->output)
  202.           fprintf (output, "%s", formatter->output);
  203.         formatter->destroy (formatter);
  204.       }
  205.       fclose (output);
  206.     }
  207.  
  208.     /* deal with errors */
  209.     else
  210.       errors->set_code (errors, E_FILE_NOT_FOUND, 0, 0);
  211.  
  212.     /* free the output filename */
  213.     free (output_filename);
  214.   }
  215.  
  216.   /* deal with out of memory error */
  217.   else
  218.     errors->set_code (errors, E_MEMORY, 0, 0);
  219. }
  220.  
  221. /*
  222.  * Output a C source file
  223.  * params:
  224.  *   ProgramNode*   program   the parsed program
  225.  */
  226. static void output_c (ProgramNode *program) {
  227.  
  228.   /* local variables */
  229.   FILE *output; /* the output file */
  230.   char *output_filename; /* the output filename */
  231.   CProgram *c_program; /* the C program */
  232.  
  233.   /* open the output file */
  234.   output_filename = malloc (strlen (input_filename) + 5);
  235.   sprintf (output_filename, "%s.c", input_filename);
  236.   if ((output = fopen (output_filename, "w"))) {
  237.  
  238.     /* write to the output file */
  239.     c_program = new_CProgram (errors, loptions);
  240.     if (c_program) {
  241.       c_program->generate (c_program, program);
  242.       if (c_program->c_output)
  243.         fprintf (output, "%s", c_program->c_output);
  244.       c_program->destroy (c_program);
  245.     }
  246.     fclose (output);
  247.   }
  248.  
  249.   /* deal with errors */
  250.   else
  251.     errors->set_code (errors, E_FILE_NOT_FOUND, 0, 0);
  252.  
  253.   /* clean up allocated memory */
  254.   free (output_filename);
  255. }
  256.  
  257. /*
  258.  * Invoke a compiler to turn a C source file into an executable
  259.  * params:
  260.  *   char*   basic_filename   The BASIC program's name
  261.  */
  262. static void output_exe (char *command, char *basic_filename) {
  263.  
  264.   /* local variables */
  265.   char
  266.     c_filename[256], /* the name of the C source */
  267.     exe_filename[256], /* the base name of the executable */
  268.     final_command[1024], /* the constructed compiler command */
  269.     *ext, /* position of extension character '.' in filename */
  270.     *src, /* source pointer for string copying */
  271.     *dst; /* destination pointer for string copying */
  272.  
  273.   /* work out the C and EXE filenames */
  274.   sprintf (c_filename, "%s.c", basic_filename);
  275.   strcpy (exe_filename, basic_filename);
  276.   if ((ext = strchr (exe_filename, '.')))
  277.     *ext = '\0';
  278.   else
  279.     strcat (exe_filename, ".out");
  280.  
  281. #ifndef _KOLIBRI
  282.   /* build the compiler command */
  283.   src = command;
  284.   dst = final_command;
  285.   while (*src) {
  286.     if (! strncmp (src, "$(TARGET)", strlen ("$(TARGET)"))) {
  287.       strcpy (dst, exe_filename);
  288.       dst += strlen (exe_filename);
  289.       src += strlen ("$(TARGET)");
  290.     } else if (! strncmp (src, "$(SOURCE)", strlen ("$(SOURCE)"))) {
  291.       strcpy (dst, c_filename);
  292.       dst += strlen (c_filename);
  293.       src += strlen ("$(SOURCE)");
  294.     } else
  295.       *(dst++) = *(src++);
  296.   }
  297.   *dst = '\0';
  298.  
  299.   /* run the compiler command */
  300.   system (final_command);
  301. #else
  302.   sprintf(final_command, KTCC_FLAGS, c_filename, exe_filename);
  303.   if(!_ksys_exec(KTCC_BIN, final_command)){
  304.     printf("Bad command: %s %s\n", KTCC_BIN, final_command);
  305.     exit(0);
  306.   }
  307. #endif
  308. }
  309.  
  310. /*
  311.  * Top Level Routine
  312.  */
  313.  
  314.  
  315. /*
  316.  * Main Program
  317.  * params:
  318.  *   int     argc   number of arguments on the command line
  319.  *   char**  argv   the arguments
  320.  * returns:
  321.  *   int            any error code from processing/running the program
  322.  */
  323. int main (int argc, char **argv) {
  324.   /* local variables */
  325.   FILE *input; /* input file */
  326.   ProgramNode *program; /* the parsed program */
  327.   ErrorCode code; /* error returned */
  328.   Parser *parser; /* parser object */
  329.   Interpreter *interpreter; /* interpreter object */
  330.   char
  331.     *error_text, /* error text message */
  332.     *command; /* command for compilation */
  333.  
  334.   /* interpret the command line arguments */
  335.   errors = new_ErrorHandler ();
  336.   loptions = new_LanguageOptions ();
  337.   set_options (argc, argv);
  338.  
  339.   /* give usage if filename not given */
  340.   if (! input_filename) {
  341.     printf ("Usage: tinybas [OPTIONS] INPUT-FILE\n");
  342.     errors->destroy (errors);
  343.     loptions->destroy (loptions);
  344.     return 0;
  345.   }
  346.   /* otherwise attempt to open the file */
  347.   if (!(input = fopen (input_filename, "r"))) {
  348.     printf ("Error: cannot open file %s\n", input_filename);
  349.     errors->destroy (errors);
  350.     loptions->destroy (loptions);
  351.     return E_FILE_NOT_FOUND;
  352.   }
  353.  
  354.   /* get the parse tree */
  355.   parser = new_Parser (errors, loptions, input);
  356.   program = parser->parse (parser);
  357.   parser->destroy (parser);
  358.   fclose (input);
  359.  
  360.   /* deal with errors */
  361.   if ((code = errors->get_code (errors))) {
  362.     error_text = errors->get_text (errors);
  363.     printf ("Parse error: %s\n", error_text);
  364.     free (error_text);
  365.     loptions->destroy (loptions);
  366.     errors->destroy (errors);
  367.     return code;
  368.   }
  369.  
  370.   /* perform the desired action */
  371.   switch (output) {
  372.     case OUTPUT_INTERPRET:
  373.       interpreter = new_Interpreter (errors, loptions);
  374.       interpreter->interpret (interpreter, program);
  375.       interpreter->destroy (interpreter);
  376.       if ((code = errors->get_code (errors))) {
  377.         error_text = errors->get_text (errors);
  378.         printf ("Runtime error: %s\n", error_text);
  379.         free (error_text);
  380.       }
  381.       break;
  382.     case OUTPUT_LST:
  383.       output_lst (program);
  384.       break;
  385.     case OUTPUT_C:
  386.       output_c (program);
  387.       break;
  388.     case OUTPUT_EXE:
  389.      
  390.     #ifndef _KOLIBRI
  391.         if ((command = getenv ("TBEXE"))) {
  392.             output_c (program);
  393.             output_exe (command, input_filename);
  394.         } else {
  395.             printf ("TBEXE not set.\n");
  396.             break;
  397.         }
  398.     #else
  399.         output_c (program);
  400.         output_exe (NULL, input_filename);
  401.         break;
  402.     #endif
  403.   }
  404.   /* clean up and return success */
  405.   program_destroy (program);
  406.   loptions->destroy (loptions);
  407.   errors->destroy (errors);
  408.   exit(0);
  409. }
  410.