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