/* Yacc grammar for Shrdlu blocks world. */

%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define YYSTYPE tree
#define YYERROR_VERBOSE 1
#define w(X) ((void*)(X))

/* Since these may #include "shrdparse.tab.h",
   they must come after #define YYSTYPE tree */
#include "assert.h"
#include "tree.h"
#include "universe.h"
#include "why.h"

extern int suppress_yyerror;
void print_vocabulary(void);
void not_a_question(void);
void not_a_statement(void);
adjective_list build_number(int i);
%}

%token  PUT DESCRIBE QUIT
%token  A THE ANOTHER
%token  ON
%token  BLOCK TABLE RED GREEN BLUE
%token  YOU YOURSELF
%token  IT ITSELF
%token  WHY DID EVERYTHING
%token  EOL_ Q_ C_ UNKNOWN
%token  NUMBER /* 0, 1, 2, etc. */

%%

input: input something | /* empty */ ;

something:
  | QUIT period EOL_ { YYACCEPT; };
  | cmd period EOL_ { verb v = $1; v->u.v.actor = build_noun(w(YOU)); perform(v); };
  | WHY DID noun_phrase cmd question EOL_ { verb v = $4; v->u.v.actor = $3; answer(build_question(w(WHY), v)); };
  | WHY question EOL_ { why_did_you(NULL, NULL, NULL); };
  | DID noun_phrase cmd question EOL_ { verb v = $3; v->u.v.actor = $2; answer(build_question(w(DID), v)); };
  | EOL_ { print_vocabulary(); };
  | error EOL_ { yyerrok; };

cmd: DESCRIBE noun_phrase { $$ = build_verb(w(DESCRIBE), NULL, $2, NULL, NULL); };
  | PUT noun_phrase ON noun_phrase { $$ = build_verb(w(PUT), NULL, $2, w(ON), $4); };

period: C_ ;
  | Q_ { not_a_question(); YYERROR; };
  | /* empty */ ;

question: Q_ ;
  | C_ { not_a_statement(); YYERROR; };
  | /* empty */ ;

article: A { $$ = w(A); };
  | THE { $$ = w(THE); };
  | ANOTHER { $$ = w(ANOTHER); };

adjective: RED { $$ = build_adjective(w(RED)); };
  | GREEN { $$ = build_adjective(w(GREEN)); };
  | BLUE { $$ = build_adjective(w(BLUE)); };

noun: BLOCK    { $$ = build_noun(w(BLOCK)); };
  | TABLE      { $$ = build_noun(w(TABLE)); };
  | adjective BLOCK { $$ = append_noun(build_noun(w(BLOCK)), $1); };

noun_phrase:  article noun { $$ = append_noun($2, $1); };
  | BLOCK number_adj { assert(($2)->word == NULL); $$ = append_noun(build_noun(w(BLOCK)), $2); };
  | number_adj { assert(($1)->word == NULL); $$ = append_noun(build_noun(w(BLOCK)), $1); };
  | YOU { $$ = build_noun(w(YOU)); };
  | YOURSELF { $$ = build_noun(w(YOURSELF)); };
  | IT { $$ = build_noun(w(IT)); };
  | ITSELF { $$ = build_noun(w(ITSELF)); };
  | EVERYTHING { $$ = build_noun(w(EVERYTHING)); };
  | EVERYTHING adjective { $$ = append_noun(build_noun(w(EVERYTHING)), $2); };

number_adj: NUMBER { $$ = $1; };

%%

#include <stdio.h>
#include <stdlib.h>
#include "why.h"

int suppress_yyerror = 0;

int main(void)
{
    init_universe();
    yyparse();
    return 0;
}

int yyerror(char *error)
{
    if (suppress_yyerror) {
        suppress_yyerror = 0;
        return 0;
    }
    /* The lexer takes care of UNKNOWN errors for us! */
    if (strstr(error, "UNKNOWN") != NULL)
      return 0;
    puts("I DIDN'T UNDERSTAND THAT COMBINATION OF WORDS.");
    set_why_context(why_dummy, NULL, NULL);
    new_reason(why_dummy, NULL, NULL,
        WHY_GRAMMAR, NULL, NULL);
    return 0;
}

void print_vocabulary(void)
{
    static int msg = 2;
    if (!msg) return;
    msg--;
    if (msg == 1) {
        puts("(Blank line was ignored.)");
    }
    else {
        puts("(Shrdlu recognizes English commands such as:");
        puts("PUT BLOCK 2 ON ANOTHER BLOCK");
        puts("DESCRIBE IT");
        puts("DESCRIBE EVERYTHING");
        puts("WHY DID YOU PUT BLOCK 7 ON THE TABLE?");
        puts("QUIT");
        puts("This is all the help you're getting from me.");
        puts("I'm going back to ignoring blank lines.)");
    }
}

void not_a_question(void)
{
    puts("THAT DIDN'T LOOK LIKE A QUESTION.");
    set_why_context(why_dummy, NULL, NULL);
    new_reason(why_dummy, NULL, NULL,
       WHY_GRAMMAR, NULL, NULL);
}

void not_a_statement(void)
{
    puts("THAT DIDN'T LOOK LIKE A STATEMENT.");
    new_reason(why_dummy, NULL, NULL,
       WHY_GRAMMAR, NULL, NULL);
}

/* used in the lexer */
adjective_list build_number(int i)
{
    adjective_list a = malloc(sizeof *a);
    assert(a != NULL);
    a->pos = ADJECTIVE_LIST;
    a->word = NULL;
    a->u.al.number = i;
    a->u.al.adjlist = NULL;
    return a;
}