Subversion Repositories Kolibri OS

Rev

Rev 8733 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
8733 turbocat 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 
12
#include 
13
#include 
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
9234 turbocat 23
    #include 
8733 turbocat 24
    #define KTCC_BIN "/kolibrios/develop/tcc/tcc"
9234 turbocat 25
    #define KTCC_FLAGS "-nobss %s -o %s -ltcc -lc.obj"
8733 turbocat 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
}