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

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

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

LET x = 5
IF x THEN
        PRINT x
        IF x + 2 THEN
                PRINT x + 2
        ENDIF
ENDIF

/////////////
// basic.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);
%}
 
%union { 
    char *s; 
}
 
%token LET PRINT IF THEN ELSE ENDIF EOL 
%token <s> ATOM
%type <s> expr
 
%left '+' 
 
%%
 
program
  : /* empty */
  | program line
  ;
 
line
  : LET ATOM '=' expr EOL
      { printf("var %s = %s;\n", $2, $4); }
  | PRINT expr EOL
      { printf("console.log(%s);\n", $2); }
  | if_stmt
  | EOL
      { }
  ;
 
if_stmt
  : IF expr THEN EOL
        { printf("if (%s) {\n\t", $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); 
}

////////////
// basic.l
////////////

%option noyywrap 
%option yylineno
 
%{
#include "basic.tab.h"
#include <string.h>
%}
 
ID   [A-Za-z_][A-Za-z0-9_]*
NUM  [0-9]+
 
%%
 
"LET"        return LET;
"PRINT"      return PRINT;
"IF"         return IF;
"THEN"       return THEN;
"ELSE"       return ELSE;
"ENDIF"      return ENDIF;
 
{ID}         { yylval.s = strdup(yytext); return ATOM; }
{NUM}        { yylval.s = strdup(yytext); return ATOM; }
 
"="          return '=';
"\n"         return EOL;
 
[ \t\r]+     ;
.            return yytext[0];
 
%%