/* Kroparser -- parser muzyki, zapisanej brajlowskimi kropami
 * Kolejnosc przetwarzania:
 *  (1) bison kroparser.y
 *  (2) gcc -Wall -o kroparser kroparser.tab.c
 * Wywolanie:
 *      ./kroparser nuty.wej nuty.wyj
 * Plik  nuty.wej  ma byc wypelniony dwucyfrowymi liczbami osemkowymi,
 * oznaczajacymi szesciokropasy;  nuty.wyj  nie moze byc nazwa zadnego
 * istniejacego pliku (pod grozba jego zniszczenia).
 */
     
%{

  #include <ctype.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>

  #define YYERROR_VERBOSE 1
  #define DL_KOMUNIKATU 30
  #define DL_METRUM 10
  #define DL_NUTY 20
  #define IL_NUT_W_TAKCIE 100
  #define DL_NAZWY_PLIKU 30

  typedef  enum {
    subkontra = 0,
    kontra = 1,
    wielka = 2,
    mala = 3,
    razkreslna = 4,
    dwukreslna = 5,
    trojkreslna = 6,
    czterkreslna = 7,
    pieckreslna = 8,
    zadna = 9,
  }  Oktawa;

  typedef  enum {
    ce = 0,
    de = 1,
    ee = 2,
    ef = 3,
    ge = 4,
    aa = 5,
    ha = 6,
    pau = 7,
    nic = 8
  }  Wysokosc_dzwieku;

  typedef  enum {
    krotkie = 0, dlugie = 1
  }  Dlugosc_nut;

  int  yylex (void);

  void  wys_i_trwanie(char nuta[DL_NUTY], Wysokosc_dzwieku dz, int trwanie);

  FILE* wej;
  FILE* wyj;
  char  zn_chrom, zn_okt,
        metrum[DL_METRUM],
        nuta[DL_NUTY],
        takt[DL_NUTY * IL_NUT_W_TAKCIE];
  int  nr_kropasa, il_krop, i;
  Oktawa akt_oktawa;
  Wysokosc_dzwieku akt_wys;
  Dlugosc_nut dlugosci;

  void  yyerror (char *s) {  // wywolywane przez  yyparse  w razie bledu
    printf ("\n !!! kropas nr %i: %s\n\n", nr_kropasa, s);
    exit(1);
  }

%}

%left p00
%token p01
%token p02
%token p03
%token p04
%token p05
%token p06
%token p10
%token p11
%token p12
%token p13
%token p15
%token p16
%token p17
%token p20
%token p21
%token p22
%token p23
%token p24
%token p25
%token p26
%token p27
%token p30
%token p31
%token p32
%token p33
%token p34
%token p35
%token p36
%token p37
%token p40
%token p41
%token p42
%right p43
%token p45
%token p46
%token p47
%token p50
%token p51
%token p52
%token p53
%token p55
%token p56
%token p57
%token p60
%token p61
%token p62
%token p63
%token p64
%token p65
%token p66
%token p67
%token p70
%token p71
%token p72
%token p73
%token p74
%token p75
%token p76
%token p77

%glr-parser

%%

    /* Gramatyka: */

Muzyka  :  Klucz TonacjaMetrum Takty
                   { fprintf(wyj, " ||\n\n"); fclose(wej); fclose(wyj); }
;

Klucz  : /* empty */
       |  p34                        { fprintf(wyj, "  klucz"); }
;

TonacjaMetrum : Metrum             { fprintf(wyj, "  %s", metrum); }
              | p51 Metrum         { fprintf(wyj, "  tonacja:1#  %s", metrum); }
              | p51 p51 Metrum     { fprintf(wyj, "  tonacja:2#  %s", metrum); }
              | p51 p51 p51 Metrum { fprintf(wyj, "  tonacja:3#  %s", metrum); }
              | Liczba p51 Metrum  { fprintf(wyj, "  tonacja:%i#  %s", $1, metrum); }
              | p43 Metrum         { fprintf(wyj, "  tonacja:1b  %s", metrum); }
              | p43 p43 Metrum     { fprintf(wyj, "  tonacja:2b  %s", metrum); }
              | p43 p43 p43 Metrum { fprintf(wyj, "  tonacja:3b  %s", metrum); }
              | Liczba p43 Metrum  { fprintf(wyj, "  tonacja:%ib  %s", $1, metrum); }
;

Liczba  :  p74 Cyfra                 { $$ = $2; }
        |  Liczba Cyfra              { $$ = $1*10 + $2; }
;

Cyfra  :  p32              { $$ = 0; }
       |  p01              { $$ = 1; }
       |  p03              { $$ = 2; }
       |  p11              { $$ = 3; }
       |  p31              { $$ = 4; }
       |  p21              { $$ = 5; }
       |  p13              { $$ = 6; }
       |  p33              { $$ = 7; }
       |  p23              { $$ = 8; }
       |  p12              { $$ = 9; }
;

Metrum  :  Liczba Liczba_obnizona    { sprintf(metrum, "metrum:%i/%i\n  ", $1, $2); }
        |  Liczba                    { sprintf(metrum, "metrum:%i\n ", $1); }
;

Liczba_obnizona  :  Cyfra_obnizona
                 |  Liczba_obnizona Cyfra_obnizona    { $$ = $1*10 + $2; }
;

Cyfra_obnizona  :  p64     { $$ = 0; }
                |  p02     { $$ = 1; }
                |  p06     { $$ = 2; }
                |  p22     { $$ = 3; }
                |  p62     { $$ = 4; }
                |  p42     { $$ = 5; }
                |  p26     { $$ = 6; }
                |  p66     { $$ = 7; }
                |  p46     { $$ = 8; }
                |  p24     { $$ = 9; }
;

Takty  :  Takty1 p43 p05
       |  Takty1 p00 Takt p43 p05
;

Takty1  :  Takt            { fprintf(wyj, " |\n  "); }
        |  Takty1 p00 Takt { fprintf(wyj, " |\n  "); }
;

Takt  :  Nuta             { fprintf(wyj, " %s", nuta); strcpy(nuta, ""); }
      |  Takt Nuta        { fprintf(wyj, " %s", nuta); strcpy(nuta, ""); }
;

Nuta  :  nutka Kropki                   { while (il_krop > 0) {
                                            il_krop--; strcat(nuta, ".");
                                        }
                                        }
      |  znak_chrom nutka Kropki        { for (i=strlen(nuta); i>=0; i--)
                                            nuta[i+3]=nuta[i];
                                          nuta[0] = '(';
                                          nuta[1] = zn_chrom;
                                          nuta[2] = ')';
                                          while (il_krop > 0) {
                                            il_krop--; strcat(nuta, ".");
                                          }
                                        }
      |  znak_oktawy nutka Kropki       { while (il_krop > 0) {
                                            il_krop--; strcat(nuta, ".");
                                          }
                                        }
      |  znak_chrom znak_oktawy nutka Kropki
                                        { for (i=strlen(nuta); i>=0; i--)
                                            nuta[i+3]=nuta[i];
                                          nuta[0] = '(';
                                          nuta[1] = zn_chrom;
                                          nuta[2] = ')';
                                          while (il_krop > 0) {
                                            il_krop--; strcat(nuta, ".");
                                          }
                                        }
;

znak_chrom  :  p51          { zn_chrom = '#'; }
            |  p43          { zn_chrom = 'b'; }
            |  p41          { zn_chrom = '!'; }
;

znak_oktawy  :  p10 p10   { akt_oktawa = subkontra; akt_wys = nic; }
             |  p10       { akt_oktawa = kontra; akt_wys = nic; }
             |  p30       { akt_oktawa = wielka; akt_wys = nic; }
             |  p70       { akt_oktawa = mala; akt_wys = nic; }
             |  p20       { akt_oktawa = razkreslna; akt_wys = nic; }
             |  p50       { akt_oktawa = dwukreslna; akt_wys = nic; }
             |  p60       { akt_oktawa = trojkreslna; akt_wys = nic; }
             |  p40       { akt_oktawa = czterkreslna; akt_wys = nic; }
             |  p40 p40   { akt_oktawa = pieckreslna; akt_wys = nic; }
;

nutka  :  nutka_wlasc
       |  zn_dlug nutka_wlasc
;

zn_dlug  :      p43 p02   { dlugosci = 1-dlugosci; }
         |  p30 p43 p02   { dlugosci = dlugie; }
         |  p10 p43 p02   { dlugosci = krotkie; }
;

nutka_wlasc  :  p75          { wys_i_trwanie(nuta, ce, 1); }
             |  p65          { wys_i_trwanie(nuta, de, 1); }
             |  p57          { wys_i_trwanie(nuta, ee, 1); }
             |  p77          { wys_i_trwanie(nuta, ef, 1); }
             |  p67          { wys_i_trwanie(nuta, ge, 1); }
             |  p56          { wys_i_trwanie(nuta, aa, 1); }
             |  p76          { wys_i_trwanie(nuta, ha, 1); }
             |  p15          { wys_i_trwanie(nuta, pau, 1); }
         //----------------------------------------
             |  p35          { wys_i_trwanie(nuta, ce, 2); }
             |  p25          { wys_i_trwanie(nuta, de, 2); }
             |  p17          { wys_i_trwanie(nuta, ee, 2); }
             |  p37          { wys_i_trwanie(nuta, ef, 2); }
             |  p27          { wys_i_trwanie(nuta, ge, 2); }
             |  p16          { wys_i_trwanie(nuta, aa, 2); }
             |  p36          { wys_i_trwanie(nuta, ha, 2); }
             |  p45          { wys_i_trwanie(nuta, pau, 2); }
         //----------------------------------------
             |  p71          { wys_i_trwanie(nuta, ce, 4); }
             |  p61          { wys_i_trwanie(nuta, de, 4); }
             |  p53          { wys_i_trwanie(nuta, ee, 4); }
             |  p73          { wys_i_trwanie(nuta, ef, 4); }
             |  p63          { wys_i_trwanie(nuta, ge, 4); }
             |  p52          { wys_i_trwanie(nuta, aa, 4); }
             |  p72          { wys_i_trwanie(nuta, ha, 4); }
             |  p47          { wys_i_trwanie(nuta, pau, 4); }
         //----------------------------------------
             |  p31          { wys_i_trwanie(nuta, ce, 8); }
             |  p21          { wys_i_trwanie(nuta, de, 8); }
             |  p13          { wys_i_trwanie(nuta, ee, 8); }
             |  p33          { wys_i_trwanie(nuta, ef, 8); }
             |  p23          { wys_i_trwanie(nuta, ge, 8); }
             |  p12          { wys_i_trwanie(nuta, aa, 8); }
             |  p32          { wys_i_trwanie(nuta, ha, 8); }
             |  p55          { wys_i_trwanie(nuta, pau, 8); }
;

Kropki : /* empty */      { il_krop=0; }
       | Kropki p04       { il_krop++; }
;

%%

/****************************************************/

char  getch () {
  char  z = getc(wej);
  if (z == EOF)  printf("\n");
  else  printf("%c", z);
  return z;
}


int  yylex (void) {
  char  c1, c2;  char komunikat[DL_KOMUNIKATU];
  c2 = ' ';
  do {
    c1 = c2; c2 = getch();
    if (c1 == '/' && c2 == '/') {
      do {
        c1 = getch();
      } while (c1 != '\n');
      c1 = ' '; c2 = ' ';
    }
  } while (isspace(c1));
  nr_kropasa++;
  if (c2 == EOF)  return -1;
  switch (c1) {
    case '0':
      switch (c2) {
        case '0':  return p00;
        case '1':  return p01;
        case '2':  return p02;
        case '3':  return p03;
        case '4':  return p04;
        case '5':  return p05;
        case '6':  return p06;
        default:
          sprintf(komunikat, "nieznany kropas %c%c", c1, c2);
          yyerror(komunikat); return 1;
      }
    case '1':
      switch (c2) {
        case '0':  return p10;
        case '1':  return p11;
        case '2':  return p12;
        case '3':  return p13;
        case '5':  return p15;
        case '6':  return p16;
        case '7':  return p17;
        default:
          sprintf(komunikat, "nieznany kropas %c%c", c1, c2);
          yyerror(komunikat); return 1;
      }
    case '2':
      switch (c2) {
        case '0':  return p20;
        case '1':  return p21;
        case '2':  return p22;
        case '3':  return p23;
        case '4':  return p24;
        case '5':  return p25;
        case '6':  return p26;
        case '7':  return p27;
        default:
          sprintf(komunikat, "nieznany kropas %c%c", c1, c2);
          yyerror(komunikat); return 1;
      }
    case '3':
      switch (c2) {
        case '0':  return p30;
        case '1':  return p31;
        case '2':  return p32;
        case '3':  return p33;
        case '4':  return p34;
        case '5':  return p35;
        case '6':  return p36;
        case '7':  return p37;
        default:
      	  sprintf(komunikat, "nieznany kropas %c%c", c1, c2);
          yyerror(komunikat); return 1;
      }
    case '4':
      switch (c2) {
        case '0':  return p40;
        case '1':  return p41;
        case '2':  return p42;
        case '3':  return p43;
        case '5':  return p45;
        case '6':  return p46;
        case '7':  return p47;
        default:
      	  sprintf(komunikat, "nieznany kropas %c%c", c1, c2);
      	  yyerror(komunikat); return 1;
      }
    case '5':
      switch (c2) {
        case '0':  return p50;
        case '1':  return p51;
        case '2':  return p52;
        case '3':  return p53;
        case '5':  return p55;
        case '6':  return p56;
        case '7':  return p57;
        default:
    	  sprintf(komunikat, "nieznany kropas %c%c", c1, c2);
    	  yyerror(komunikat); return 1;
      }
    case '6':
      switch (c2) {
        case '0':  return p60;
        case '1':  return p61;
        case '2':  return p62;
        case '3':  return p63;
        case '4':  return p64;
        case '5':  return p65;
        case '6':  return p66;
        case '7':  return p67;
        default:
      	  sprintf(komunikat, "nieznany kropas %c%c", c1, c2);
    	  yyerror(komunikat); return 1;
      }
    case '7':
      switch (c2) {
        case '0':  return p70;
        case '1':  return p71;
        case '2':  return p72;
        case '3':  return p73;
        case '4':  return p74;
        case '5':  return p75;
        case '6':  return p76;
        case '7':  return p77;
        default:
      	  sprintf(komunikat, "nieznany kropas %c%c", c1, c2);
    	  yyerror(komunikat); return 1;
      }
    default:
      sprintf(komunikat, "nieznany kropas %c%c", c1, c2);
      yyerror(komunikat); return 1;
  }
}

/****************************************************/

void  wys_i_trwanie(char nuta[DL_NUTY], Wysokosc_dzwieku dz, int trwanie) {
  if (akt_oktawa == zadna)  yyerror("nieustalona oktawa");
  else {
    char trw_napis[DL_NUTY];
    if (akt_wys != nic) { // nie ma znaku oktawy
      if ((akt_wys == aa && dz == ce) ||
          (akt_wys == ha && (dz == ce || dz == de))
	 )
        akt_oktawa++;
      if ((akt_wys == de && dz == ha) ||
          (akt_wys == ce && (dz == aa || dz == ha))
	 )
        akt_oktawa--;
      if (akt_oktawa < subkontra)  yyerror("za niska oktawa");
      if (akt_oktawa > pieckreslna) yyerror("za wysoka oktawa");
    }
    switch (dz) {
      case ce:  strcat(nuta, "C"); break;
      case de:  strcat(nuta, "D"); break;
      case ee:  strcat(nuta, "E"); break;
      case ef:  strcat(nuta, "F"); break;
      case ge:  strcat(nuta, "G"); break;
      case aa:  strcat(nuta, "A"); break;
      case ha:  strcat(nuta, "H"); break;
      case pau: strcat(nuta, "-"); break;
      default: yyerror("co to za nuta?");
    }
    if (dz != pau) {
      akt_wys = dz;
      strcat(nuta, "{");
      switch (akt_oktawa) {
        case subkontra:    strcat(nuta, "-3"); break;
        case kontra:       strcat(nuta, "-2"); break;
        case wielka:       strcat(nuta, "-1"); break;
        case mala:         strcat(nuta, "0"); break;
        case razkreslna:   strcat(nuta, "+1"); break;
        case dwukreslna:   strcat(nuta, "+2"); break;
        case trojkreslna:  strcat(nuta, "+3"); break;
        case czterkreslna: strcat(nuta, "+4"); break;
        case pieckreslna:  strcat(nuta, "+5"); break;
        default: yyerror("co to za oktawa?");
      }
      strcat(nuta, "}");
    }
    if (dlugosci == dlugie)
      sprintf(trw_napis, "%i", trwanie);
    else
      sprintf(trw_napis, "%i", 16*trwanie);
    strcat(nuta, "[1/"); strcat(nuta, trw_napis); strcat(nuta, "]");
  }
}

/****************************************************/

int  main (int argn, char* arg[]) {
  char  plik_wej[DL_NAZWY_PLIKU], plik_wyj[DL_NAZWY_PLIKU];
  if  (argn == 3) {
    strcpy(plik_wej, arg[1]); strcpy(plik_wyj, arg[2]);
  }
  else  yyerror("WYWOLANIE:  kroparser plik_wej plik_wyj");

  wej = fopen(plik_wej, "r");
  wyj = fopen(plik_wyj, "w");
  akt_oktawa = zadna; akt_wys = nic;
  strcpy(metrum, ""); strcpy(nuta, ""); 
  nr_kropasa = 0; il_krop = 0; dlugosci = dlugie;
  printf("\n"); fprintf(wyj, "\n");

  return  yyparse();

}
