1%{
2/*
3
4    YABASIC  ---  a simple Basic Interpreter
5    written by Marc Ihm 1995-2021
6    more info at www.yabasic.de
7
8    FLEX part
9
10    This file is part of yabasic and may be copied under the terms of
11    MIT License which can be found in the file LICENSE.
12
13*/
14
15#include <string.h>
16
17#include "bison.h"       /* get tokens from BISON */
18#ifndef YABASIC_INCLUDED
19#include "yabasic.h"     /* definitions of yabasic */
20#endif
21int import_lib(char *); /* import library */
22
23#define MAX_INCLUDE_DEPTH 5
24#define MAX_INCLUDE_NUMBER 100
25static YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; /* stack for included libraries */
26int include_depth; /* current position in library_stack */
27struct library *library_stack[MAX_INCLUDE_DEPTH]; /* stack for library file names */
28int library_chain_length=0; /* length of library_chain */
29struct library *library_chain[MAX_INCLUDE_NUMBER]; /* list of all library file names in order of appearance */
30struct library *currlib; /* current library as relevant to bison */
31int inlib; /* true, while in library */
32int in_short_if=0; /* true, if within a short if */
33int len_of_lineno=0; /* length of last line number */
34YY_BUFFER_STATE from_string_buffer; /* to read from string */
35
36/*
37    Remark on yycolumn and yylineno:
38
39    We only need to track yycolumn, yylineno is taken care for us
40    entirely by flex (even during yyless).  To handle yycolumn we need
41    the YY_USER_ACTION which advances yycolumn by the length of each
42    token; additionally we reset it to 1 every time we see a newline.
43
44    The newline normally will be returned as token tSEP; only if the
45    newline terminates a short if-statement (which has no endif), the
46    token tIMPLICITENDIF will be returned. In any case we reset
47    yycolumn appropriately.
48
49    Bison, which consumes the tokens returned by this lexer, often
50    needs to look ahead a few (two ?) tokens to parse correctly, this
51    might trigger the lexer to read into the next line, which will in
52    turn reset yycolumn.
53
54    This needs to be handled correctly in effective_lineno(), which
55    returns the current line number. There are also quite a lot of
56    tests now, which make sure, that the linenumber for error messages
57    is updated correctly.
58
59*/
60
61int yycolumn=1;
62int yydoublenl;
63#define YY_USER_ACTION yydoublenl=FALSE;yylloc.first_line=yylloc.last_line=yylineno; yylloc.first_column=yycolumn; yylloc.last_column=yycolumn+yyleng-1;yycolumn+=yyleng;
64
65int start_token;
66%}
67
68WS [ \t\f\r\v]
69NAME ([a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*)|([a-z_][a-z0-9_]*)
70
71%option noyywrap
72%option yylineno
73%x PRELNO
74%x PASTLNO
75%x PASTIMPORT
76%x EVAL_DIGITS
77
78%%
79%{
80  if (start_token != evNONE)
81      {
82        int t = start_token;
83        start_token = evNONE;
84        return t;
85      }
86%}
87
88<<EOF>> {
89  if (severity_threshold <= sDEBUG) {
90    sprintf(string,"Closing file '%s'",currlib->short_name);
91    error(sDEBUG,string);
92  }
93  if (--include_depth<0) {
94    return tEOPROG;
95  } else {
96    if (!is_bound) {
97      yy_delete_buffer(YY_CURRENT_BUFFER);
98      yy_switch_to_buffer(include_stack[include_depth]);
99    }
100    report_if_missing("Premature end of file",TRUE);
101    leave_lib();
102    return tSEP;
103  }
104}
105
106{WS}+ {BEGIN(INITIAL);}     /* ignore whitespace */
107
108^{WS}*/[0-9]+ {if (program_state<=spCOMPILING) {BEGIN(PRELNO);return tLABEL;} else {BEGIN(EVAL_DIGITS);}} /* for functions eval() and compile(), when program state has advanced to spRUNNING, we do not accept line numbers */
109<PRELNO>[0-9]+ {
110  BEGIN(PASTLNO);
111  yylval.symbol=(char *)my_strdup(yytext);
112  len_of_lineno=strlen(yytext);
113  return tSYMBOL;
114}
115<PASTLNO>.* {yycolumn=len_of_lineno+1;BEGIN(INITIAL);yyless(0);return tSEP;}
116<PASTLNO>\n {yycolumn=1;BEGIN(INITIAL);return tSEP;}
117
118\n\n {yycolumn=1;yydoublenl=TRUE; if (in_short_if) {in_short_if--;yyless(0);return tIMPLICITENDIF;} if (interactive && !inlib) {return tEOPROG;} else {return tSEP;}}
119\n {yycolumn=1; if (in_short_if) {in_short_if--;yyless(0);return tIMPLICITENDIF;};return tSEP;}
120: {if (in_short_if && check_compat) error(sWARNING,"Short if has changed in version 2.71");return tSEP;}
121
122REM{WS}+.* {return tSEP;}  /* comments span 'til end of line */
123\/\/.* {return tSEP;}  /* comments span 'til end of line */
124REM\n {yycolumn=1; if (in_short_if) {in_short_if--;yyless(0);return tIMPLICITENDIF;};return tSEP;}
125REM {yymore();}
126
127IMPORT{WS}+{NAME} {BEGIN(PASTIMPORT);import_lib(my_strdup(yytext+7));return tIMPORT;}
128<PASTIMPORT>.* {yycolumn=1;BEGIN(INITIAL);yyless(0);unput('\n');return tSEP;}
129<PASTIMPORT>\n {yycolumn=1;BEGIN(INITIAL);return tSEP;}
130
131((DOCU|DOC|DOCUMENTATION)({WS}+.*)?) {
132  char *where=strpbrk(yytext," \t\r\f\v");
133  yylval.docu=(char *)my_strdup(where ? where+1 : NULL);
134  return tDOCU;
135}
136
137^#.*\n {yycolumn=1;return tSEP;} /* hash (#) as first character of a line may introduce comments too */
138^'.*\n {yycolumn=1;return tSEP;} /* apostrophe (') as first character may introduce comments too */
139
140EXECUTE return tEXECUTE;
141"EXECUTE$" return tEXECUTE2;
142COMPILE return tCOMPILE;
143EVAL return tEVAL;
144"EVAL$" return tEVAL2;
145RUNTIME_CREATED_SUB return tRUNTIME_CREATED_SUB;
146END{WS}+SUB return tENDSUB;
147END{WS}+IF return tENDIF;
148END-IF return tENDIF;
149END{WS}+WHILE return tWEND;
150END-WHILE return tWEND;
151END{WS}+SWITCH return tSEND;
152END-SWITCH return tSEND;
153END{WS}+"SWITCH$" return tSEND;
154END-"SWITCH$" return tSEND;
155EXPORT return tEXPORT;
156ERROR return tERROR;
157FOR return tFOR;
158BREAK return tBREAK;
159SWITCH return tSWITCH;
160CASE return tCASE;
161DEFAULT return tDEFAULT;
162LOOP return tLOOP;
163DO return tDO;
164TO return tTO;
165AS return tAS;
166READING return tREADING;
167WRITING return tWRITING;
168STEP return tSTEP;
169NEXT return tNEXT;
170WHILE return tWHILE;
171WEND return tWEND;
172REPEAT return tREPEAT;
173UNTIL return tUNTIL;
174GOTO return tGOTO;
175GOSUB return tGOSUB;
176SUB return tSUB;
177SUBROUTINE return tSUB;
178LOCAL return tLOCAL;
179STATIC return tSTATIC;
180ON return tON;
181INTERRUPT return tINTERRUPT;
182CONTINUE return tCONTINUE;
183LABEL return tLABEL;
184IF return tIF;
185THEN return tTHEN;
186ELSE return tELSE;
187ELSIF return tELSIF;
188ELSEIF return tELSIF;
189ENDIF return tENDIF;
190FI return tENDIF;
191OPEN return tOPEN;
192CLOSE return tCLOSE;
193SEEK return tSEEK;
194TELL return tTELL;
195PRINT return tPRINT;
196USING return tUSING;
197REVERSE return tREVERSE;
198COLOR return tCOLOUR;
199COLOUR return tCOLOUR;
200BACKCOLOR return tBACKCOLOUR;
201BACKCOLOUR return tBACKCOLOUR;
202\? return tPRINT;
203INPUT return tINPUT;
204RETURN return tRETURN;
205DIM return tDIM;
206REDIM return tDIM;
207END return tEND;
208EXIT return tEXIT;
209READ return tREAD;
210DATA return tDATA;
211RESTORE return tRESTORE;
212AND return tAND;
213OR return tOR;
214NOT return tNOT;
215BITNOT return tBITNOT;
216EOR return tEOR;
217XOR return tEOR;
218SHL return tSHL;
219SHR return tSHR;
220WINDOW return tWINDOW;
221ORIGIN return tORIGIN;
222PRINTER return tPRINTER;
223DOT return tDOT;
224LINE return tLINE;
225CURVE return tCURVE;
226CIRCLE return tCIRCLE;
227TRIANGLE return tTRIANGLE;
228CLEAR return tCLEAR;
229FILL return tFILL;
230FILLED return tFILL;
231TEXT return tTEXT;
232RECTANGLE return tRECT;
233RECT return tRECT;
234BOX return tRECT;
235BITBLIT return tPUTBIT;
236BITBLT return tPUTBIT;
237PUTBIT return tPUTBIT;
238"BITBLT$" return tGETBIT;
239"BITBLIT$" return tGETBIT;
240"GETBIT$" return tGETBIT;
241PUTSCREEN return tPUTCHAR;
242"GETSCREEN$" return tGETCHAR;
243NEW return tNEW;
244WAIT return tWAIT;
245PAUSE return tWAIT;
246SLEEP return tWAIT;
247BELL return tBELL;
248BEEP return tBELL;
249LET return tLET;
250ARRAYDIM return tARDIM;
251ARRAYDIMENSION return tARDIM;
252ARRAYSIZE return tARSIZE;
253NUMPARAM(S)?({WS}*\({WS}*\))? {yylval.symbol=(char *)my_strdup("numparams"); return tSYMBOL;}
254BIND return tBIND;
255
256SIN return tSIN;
257ASIN return tASIN;
258COS return tCOS;
259ACOS return tACOS;
260TAN return tTAN;
261ATAN return tATAN;
262EXP return tEXP;
263LOG return tLOG;
264SQRT return tSQRT;
265SQR return tSQR;
266INT return tINT;
267CEIL return tCEIL;
268FLOOR return tFLOOR;
269ROUND return tROUND;
270FRAC return tFRAC;
271ABS return tABS;
272SIG return tSIG;
273MOD return tMOD;
274RAN return tRAN;
275MIN return tMIN;
276MAX return tMAX;
277"LEFT$" return tLEFT;
278"RIGHT$" return tRIGHT;
279"MID$" return tMID;
280"LOWER$" return tLOWER;
281"UPPER$" return tUPPER;
282"LTRIM$" return tLTRIM;
283"RTRIM$" return tRTRIM;
284"TRIM$" return tTRIM;
285INSTR return tINSTR;
286RINSTR return tRINSTR;
287"CHOMP$" return tCHOMP;
288LEN return tLEN;
289VAL return tVAL;
290EOF return tMYEOF;
291"STR$" return tSTR;
292"INKEY$" return tINKEY;
293"MOUSEX" return tMOUSEX;
294"MOUSEY" return tMOUSEY;
295"MOUSEB" return tMOUSEB;
296"MOUSEBUTTON" return tMOUSEB;
297"MOUSEMOD" return tMOUSEMOD;
298"MOUSEMODIFIER" return tMOUSEMOD;
299"CHR$" return tCHR;
300ASC return tASC;
301"HEX$" return tHEX;
302"BIN$" return tBIN;
303DEC return tDEC;
304AT return tAT;
305@ return tAT;
306SCREEN return tSCREEN;
307SYSTEM return tSYSTEM;
308"SYSTEM$" return tSYSTEM2;
309"DATE$" return tDATE;
310"TIME$" return tTIME;
311PEEK return tPEEK;
312"PEEK$" return tPEEK2;
313POKE return tPOKE;
314FOREIGN_FUNCTION_CALL return tFRNFN_CALL;
315FRNFN_CALL return tFRNFN_CALL;
316"FOREIGN_FUNCTION_CALL$" return tFRNFN_CALL2;
317"FRNFN_CALL$" return tFRNFN_CALL2;
318FOREIGN_FUNCTION_SIZE return tFRNFN_SIZE;
319FRNFN_SIZE return tFRNFN_SIZE;
320FOREIGN_BUFFER_GET return tFRNBF_GET;
321FRNBF_GET return tFRNBF_GET;
322"FOREIGN_BUFFER_GET$" return tFRNBF_GET2;
323"FRNBF_GET$" return tFRNBF_GET2;
324"FOREIGN_BUFFER_GET_BUFFER$" return tFRNBF_GET_BUFFER;
325"FRNBF_GET_BUFFER$" return tFRNBF_GET_BUFFER;
326FOREIGN_BUFFER_SET return tFRNBF_SET;
327FRNBF_SET return tFRNBF_SET;
328FOREIGN_BUFFER_SET_BUFFER return tFRNBF_SET_BUFFER;
329FRNBF_SET_BUFFER return tFRNBF_SET_BUFFER;
330"FOREIGN_BUFFER_ALLOCATE$" return tFRNBF_ALLOC;
331"FOREIGN_BUFFER_ALLOC$" return tFRNBF_ALLOC;
332"FRNBF_ALLOC$" return tFRNBF_ALLOC;
333"FOREIGN_BUFFER_DUMP$" return tFRNBF_DUMP;
334"FRNBF_DUMP$" return tFRNBF_DUMP;
335"FOREIGN_BUFFER_SIZE" return tFRNBF_SIZE;
336"FRNBF_SIZE" return tFRNBF_SIZE;
337"FOREIGN_BUFFER_FREE" return tFRNBF_FREE;
338"FRNBF_FREE" return tFRNBF_FREE;
339TOKEN return tTOKEN;
340"TOKEN$" return tTOKENALT;
341SPLIT return tSPLIT;
342"SPLIT$" return tSPLITALT;
343GLOB return tGLOB;
344"^" return tPOW;
345"**" return tPOW;
346
347"<>" return tNEQ;
348"<=" return tLEQ;
349">=" return tGEQ;
350"=" return tEQU;
351"<" return tLTN;
352">" return tGTN;
353"!" return tNOT;
354
355[-+*/:(),.;] {return yytext[0];}
356
357<INITIAL,EVAL_DIGITS>0x[0-9a-zA-Z]+ {
358  yylval.digits=(char *)my_strdup(yytext+2);
359  BEGIN(INITIAL);
360  return tHEXDIGITS;
361}
362
363<INITIAL,EVAL_DIGITS>0b[0-1]+ {
364  yylval.digits=(char *)my_strdup(yytext+2);
365  BEGIN(INITIAL);
366  return tBINDIGITS;
367}
368
369<INITIAL,EVAL_DIGITS>[0-9]+ {
370  yylval.digits=(char *)my_strdup(yytext);
371  BEGIN(INITIAL);
372  return tDIGITS;
373}
374
375<INITIAL,EVAL_DIGITS>(([0-9]+|([0-9]*\.[0-9]*))([eE][-+]?[0-9]+)?) {
376  { double d;
377    sscanf(yytext,"%lg",&d);
378    yylval.fnum=d;
379    BEGIN(INITIAL);
380    return tFNUM;
381  }
382}
383
384pi {yylval.fnum=3.1415926535897932;return tFNUM;}
385euler {yylval.fnum=2.7182818284590452;return tFNUM;}
386TRUE {yylval.fnum=1; return tFNUM;}
387FALSE {yylval.fnum=0; return tFNUM;}
388
389{NAME} {
390  yylval.symbol=(char *)my_strdup(yytext);
391  return tSYMBOL;
392}
393
394{NAME}\$ {
395  yylval.symbol=(char *)my_strdup(yytext);
396  return tSTRSYM;
397}
398
399\"[^"]*(\"|\n) {
400  if (yyleng<2 || yytext[yyleng-1]=='\n') { /* unterminated string has reached end of line, report qualified error in bison */
401        yyless(1);
402  	yylval.string=NULL;
403  	return tSTRING;
404  } else if (yytext[yyleng-1]=='\"' && count_backslashes(yytext+yyleng-2)%2==1) { /* final quote was escaped, so put all text back and read more */
405  	yyless(yyleng-1);
406	yymore();
407  } else { /* properly quoted string; remove quotes and return it */
408	yylval.string=(char *)my_strdup(yytext+1);
409	*(yylval.string+strlen(yylval.string)-1)='\0';
410	replace_escapes(yylval.string);
411	return tSTRING;
412  }
413}
414
415
416. {if (isprint(yytext[0])) return yytext[0]; else return ' ';}
417
418%%
419void open_main(FILE *file,char *explicit,char *main_file_name) /* open main file */
420{
421  include_depth=0;
422
423  if (explicit) {
424    include_stack[include_depth]=yy_scan_string(explicit);
425  } else {
426    include_stack[include_depth]=yy_create_buffer(file,YY_BUF_SIZE);
427  }
428  library_stack[include_depth]=new_library(main_file_name,"main");
429  library_chain[library_chain_length++]=library_stack[include_depth];
430  if (!explicit) yy_switch_to_buffer(include_stack[include_depth]);
431  currlib=library_stack[0];
432  inlib=FALSE;
433
434  return;
435}
436
437
438void start_flex_from_string(char *text) /* open string with commands */
439{
440  from_string_buffer = yy_scan_string(text);
441/*  yy_switch_to_buffer(from_string_buffer); */
442}
443
444
445void end_flex_from_string(void) /* free structure used to read string */
446{
447  yy_delete_buffer(from_string_buffer);
448}
449
450
451int import_lib(char *name) /* import library */
452{
453  char *full;
454  static int end_of_all_imports=FALSE;
455  static int ignore_nested_imports=FALSE;
456  FILE *lib_file;
457
458  while(isspace(*name)) name++;
459  name[strcspn(name, "\n")] = '\0';
460
461  /* This can only occur in bound programs; void all further import-statements */
462  if (!strcmp(name,"__END_OF_ALL_IMPORTS")) {
463    error(sDEBUG,"Encountered special import __END_OF_ALL_IMPORTS");
464    end_of_all_imports=TRUE;
465  }
466
467  if (end_of_all_imports) return TRUE;
468
469  /* This can only occur in bound programs, close currently imported library */
470  if (!strcmp(name,"__END_OF_CURRENT_IMPORT")) {
471    error(sDEBUG,"Encountered special import __END_OF_CURRENT_IMPORT");
472    include_depth--;
473    leave_lib();
474    ignore_nested_imports=FALSE;
475    return TRUE;
476  }
477
478  /* This can only occur in bound programs, ignore nested import-statement */
479  if (!strcmp(name,"__IGNORE_NESTED_IMPORTS")) {
480    error(sDEBUG,"Encountered special import __IGNORE_NESTED_IMPORTS");
481    ignore_nested_imports=TRUE;
482  }
483
484  if (ignore_nested_imports) return TRUE;
485
486  include_depth++;
487  inlib=TRUE;
488  if (include_depth>=MAX_INCLUDE_DEPTH) {
489    sprintf(string,"Could not import '%s': nested too deep (%d)",name,include_depth);
490    error(sERROR,string);
491    return FALSE;
492  }
493
494  if (is_bound) {
495    full=name;
496  } else {
497    lib_file=open_library(name,&full);
498    if (!lib_file) return FALSE;
499    yy_switch_to_buffer(yy_create_buffer(lib_file,YY_BUF_SIZE));
500    include_stack[include_depth]=YY_CURRENT_BUFFER;
501  }
502  library_stack[include_depth]=new_library(full,NULL);
503  library_chain[library_chain_length++]=library_stack[include_depth];
504  if (library_chain_length>=MAX_INCLUDE_NUMBER) {
505    sprintf(string,"Cannot import more than %d libraries",MAX_INCLUDE_NUMBER);
506    error(sERROR,string);
507    return FALSE;
508  }
509  if (!library_stack[include_depth]) {
510    sprintf(string,"library '%s' has already been imported",full);
511    error(sERROR,string);
512    return FALSE;
513  }
514
515  if (severity_threshold <= sNOTE) {
516    if (is_bound) {
517      sprintf(string,"importing library '%s'",name);
518    } else {
519      sprintf(string,"importing from file '%s'",full);
520    }
521    error(sNOTE,string);
522  }
523  currlib=library_stack[include_depth]; /* switch late because error() uses currlib */
524  return TRUE;
525}
526
527
528FILE *open_library(char *name,char **fullreturn) /* search and open a library */
529{
530  static char full_wdir[NAMEBUFFLEN];
531  static char full_main[NAMEBUFFLEN];
532  static char full_global[NAMEBUFFLEN];
533  char unquoted[NAMEBUFFLEN];
534  char *p;
535  FILE *lib;
536  int i;
537
538  for(p=name;strchr(" \"'`",*p);p++) if (!*p) break;
539  strncpy(unquoted,p,NAMEBUFFLEN);
540  for(;!strchr(" \"'`",*p);p++) if (!*p) break;
541  if (*p) unquoted[p-name-2]='\0';
542  name=unquoted;
543  if (strchr(name,'.')) {
544    sprintf(string,"library name '%s' contains '.'",name);
545    error(sERROR,string);
546    return NULL;
547  }
548  if (!strcmp(name,"main")) {
549    if (is_bound) return NULL;
550    error(sERROR,"invalid library name 'main'");
551    return NULL;
552  }
553
554  /* search in current working dir */
555  if (fullreturn) *fullreturn=full_wdir;
556  strncpy(full_wdir,name,NAMEBUFFLEN);
557  strcat(full_wdir,".yab");
558  lib=fopen(full_wdir,"r");
559  if (lib) return lib;
560
561  /* search in dir of main file */
562  if (fullreturn) *fullreturn=full_main;
563  if (strchr(main_file_name,'/') || strchr(main_file_name,'\\')) {
564    strncpy(full_main,main_file_name,NAMEBUFFLEN);
565  } else {
566    full_main[0]='\0';
567  }
568  for(i=0;i<2;i++) {
569    p=strrchr(full_main,"\\/"[i]);
570    if (p) *p='\0';
571  }
572  if (full_main[0] && !strchr("\\/",full_main[strlen(full_main)-1])) {
573#ifdef UNIX
574    strcat(full_main,"/");
575#else
576    strcat(full_main,"\\");
577#endif
578  }
579  strcat(full_main,name);
580  strcat(full_main,".yab");
581  lib=fopen(full_main,"r");
582  if (lib) return lib;
583
584  /* search in global directory */
585  if (fullreturn) *fullreturn=full_global;
586  strncpy(full_global,library_path,NAMEBUFFLEN);
587  if (full_global[0] && !strchr("\\/",full_global[strlen(full_global)-1])) {
588#ifdef UNIX
589    strcat(full_global,"/");
590#else
591    strcat(full_global,"\\");
592#endif
593  }
594  strcat(full_global,name);
595  strcat(full_global,".yab");
596  lib=fopen(full_global,"r");
597  if (lib) return lib;
598
599  sprintf(string,"could not open library '%s': not in current workingdir as '%s', not in directory of main file as '%s', not in library path as '%s'",name,full_wdir,full_main,full_global);
600  error(sERROR,string);
601  return NULL;
602}
603
604
605void leave_lib(void) /* processing, when end of library is found */
606{
607  if (include_depth<0) return;
608  if (severity_threshold <= sDEBUG) {
609    sprintf(string,"End of library '%s', continue with '%s', include depth is now %d",currlib->short_name,library_stack[include_depth]->short_name,include_depth);
610    error(sDEBUG,string);
611  }
612  yylineno=currlib->yylineno_at_start;
613  currlib=library_stack[include_depth];
614  inlib=(include_depth>0);
615}
616