%{

// $Id: parser_y.yy 374 2007-09-04 16:59:24Z dflater $

#include "parser.hh"

TGParser::Election election;

extern int yylex ();
extern void yyerror (const char *);

%}

%union {
  TGParser::ContestVector *contestVector;
  TGParser::Contest       *contest;
  unsigned long           number;
}

%token <number> NUMBER
%token BALLOTS DISTRICTS PRECINCTS NTOKEN MTOKEN LEVEL LOGIC DISTRIBUTION
%token D P S C R UNIFORMRANDOM TRIANGLE EVEN BALLOTDISTRIBUTION
%token PRECINCTDISTRIBUTION WTOKEN

%type <contest>       contest contestValue contestValueList
%type <contestVector> contestVector

%start election

%%

election:
  electionValueList
  | electionValueList contestVector {
    election.contests.swap (*($2));
    delete $2;
  }

electionValueList: electionValue | electionValueList electionValue;

electionValue:
  BALLOTS '=' NUMBER {
    election.ballots = $3;
  } | DISTRICTS '=' NUMBER {
    election.districts = $3;
  } | PRECINCTS '=' NUMBER {
    election.precincts = $3;
  } | BALLOTDISTRIBUTION '=' UNIFORMRANDOM {
    election.ballotDistribution = TGParser::Election::uniformRandom;
  } | BALLOTDISTRIBUTION '=' EVEN {
    election.ballotDistribution = TGParser::Election::even;
  } | PRECINCTDISTRIBUTION '=' UNIFORMRANDOM {
    election.precinctDistribution = TGParser::Election::uniformRandom;
  } | PRECINCTDISTRIBUTION '=' EVEN {
    election.precinctDistribution = TGParser::Election::even;
  }

contestVector:
  contest {
    $$ = new TGParser::ContestVector;
    $$->push_back (*($1));
    delete $1;
  } | contestVector contest {
    $$->push_back (*($2));
    delete $2;
  }

contest:
  '(' contestValueList ')' {
    $$ = $2;
  }

/* TGParser::Contest is abused to avoid the need for more data types. */

contestValueList:
  contestValue
  | contestValueList contestValue {
    // Fake a discriminated union.  Works as long as the input does
    // not specify a given field more than once (and it ought not to).
    static const TGParser::Contest defaultContest;
    if ($2->N != defaultContest.N)
      $$->N = $2->N;
    else if ($2->M != defaultContest.M)
      $$->M = $2->M;
    else if ($2->W != defaultContest.W)
      $$->W = $2->W;
    else if ($2->level != defaultContest.level)
      $$->level = $2->level;
    else if ($2->logic != defaultContest.logic)
      $$->logic = $2->logic;
    else if ($2->distribution != defaultContest.distribution)
      $$->distribution = $2->distribution;
    delete $2;
  }

contestValue:
  NTOKEN '=' NUMBER {
    $$ = new TGParser::Contest;
    $$->N = $3;
  } | MTOKEN '=' NUMBER {
    $$ = new TGParser::Contest;
    $$->M = $3;
  } | WTOKEN '=' NUMBER {
    $$ = new TGParser::Contest;
    $$->W = $3;
  } | LEVEL '=' S {
    $$ = new TGParser::Contest;
    $$->level = TGParser::Contest::systemExtent;
  } | LEVEL '=' D {
    $$ = new TGParser::Contest;
    $$->level = TGParser::Contest::district;
  } | LEVEL '=' P {
    $$ = new TGParser::Contest;
    $$->level = TGParser::Contest::precinct;
  } | LOGIC '=' NTOKEN {
    $$ = new TGParser::Contest;
    $$->logic = TGParser::Contest::NofM;
  } | LOGIC '=' C {
    $$ = new TGParser::Contest;
    $$->logic = TGParser::Contest::cumulative;
  } | LOGIC '=' R {
    $$ = new TGParser::Contest;
    $$->logic = TGParser::Contest::rankedOrder;
  } | DISTRIBUTION '=' UNIFORMRANDOM {
    $$ = new TGParser::Contest;
    $$->distribution = TGParser::Contest::uniformRandom;
  } | DISTRIBUTION '=' TRIANGLE {
    $$ = new TGParser::Contest;
    $$->distribution = TGParser::Contest::triangle;
  }
