/////////////
// Compile
/////////////

bison -d modified.y
flex modified.l
g++ modified.tab.c lex.yy.c -ll 

/////////////
// Example Inputs
/////////////

COLOR goldenrod
TEXT hello doggie
TEXT why? why is this happening to me? why!?
TEXT basketballl
BEGIN SCRIPT

    LET x = 5
    PRINT x

    IF x THEN
            PRINT x
            IF x + 2 THEN
                    PRINT x + 2
            ENDIF
    ENDIF

/////////////
// modified.y
/////////////

%define parse.error detailed
/* This line does not work in older versions of Bison,
   if you get an immediate syntax error, try this instead:
   %error-verbose
*/

%{ 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
extern int yylineno;
 
int yylex(void);
void yyerror(const char *s);
 
/* Alternative grammar */
//script
//  : script BEGINS EOL { printf("\n"); } ENDS EOL
//  | /* epsilon "script program" has many conflicts!  */
//  | EOL
//  ;
 
%}
 
%union {
  char *s;
}
 
%token LET PRINT IF THEN ELSE ENDIF EOL BEGINS ENDS
%token <s> ATOM COLOR TEXT
%type <s> expr much_text
 
%left '+'
 
%%
 
script
  : COLOR { printf("<body style=\"background-color:%s\">\n", $1); free($1); } EOL
     much_text { printf("%s",$4); free($4); }
     script 
     BEGINS EOL 
     { printf("<script>\n"); } program { printf("</script>\n"); }
  | /* empty  */
  | EOL
  ;
 
much_text
  : TEXT EOL { $$ = $1; }
  | much_text TEXT EOL
     {
      size_t n = strlen($1) + strlen($2) + 6;
      char* buf = (char*)malloc(n);
      snprintf(buf,n,"%s<br>\n%s",$1,$2);
      $$ = buf;
     }
 
program
  : /* empty */
  | program line
  ;
 
line
  : LET ATOM '=' expr EOL
      { printf("var %s = %s;\n", $2, $4); free($2); free($4); }
  | PRINT expr EOL
      { printf("console.log(%s);\n", $2); free($2); }
  | if_stmt
  | EOL
      { }
  ;
 
if_stmt
  : IF expr THEN EOL
        { printf("if (%s) {\n\t", $2); free($2); }
    block
    ENDIF EOL
        { printf("}\n"); }
 
  ;
 
block
  : /* empty */
  | block line
  ;
 
expr
  : ATOM
      { $$ = $1; }
  | expr '+' expr
      {
        size_t n = strlen($1) + strlen($3) + 4;
        char *buf = (char*)malloc(n);
        snprintf(buf, n, "%s + %s", $1, $3);
        $$ = buf;
      }   
  ;
 
%%
 
int main(void) {
    yyparse();
    return 0;
}
 
void yyerror(const char *s) {
    fprintf(stderr, "Error on line %d: %s\n", yylineno, s); 
}

////////////
// modified.l
////////////

%option noyywrap 
%option yylineno
 
%{
#include "modified.tab.h"
#include <string.h>
%}

ID [A-Za-z_][A-Za-z0-9_]*
NUM [0-9]+

%%
"BEGIN SCRIPT" return BEGINS;
"END SCRIPT"   return ENDS;
"LET"          return LET;
"PRINT"        return PRINT;
"IF"           return IF;
"THEN"         return THEN;
"ELSE"         return ELSE;
"ENDIF"        return ENDIF;
"COLOR ".*/\n  { yylval.s = strdup(yytext+6); return COLOR;}
"TEXT ".*/\n   { yylval.s = strdup(yytext+5); return TEXT;}

{ID}           { yylval.s = strdup(yytext); return ATOM; }
{NUM}          { yylval.s = strdup(yytext); return ATOM; }

"="            return '=';
"\n"           return EOL;
[ \t\r]        ;
.              return yytext[0];

%%