Subversion Repositories Kolibri OS

Rev

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