/* unity parser */
@+DELETE
/* The content of this file is not valid YACC input as it stands,
 * because of all the @ keywords.
 * These are to be processed by the program tools/stripat,
 * under the control of the Makefiles
 * in src/c and src/java.
 */
@-

%{
@!header
%}

@+c
%union {
        int i;
        float f;
        float l10f;  /* log10(f) */
        char c;
        char* s;
        struct unit_struct* u;
        struct unit_struct* uList;
        // uExpr appears only in the VOUnits case
        struct unit_expression* uExpr;
}
@-c

/* tokens with values */
%token <i> SIGNED_INTEGER UNSIGNED_INTEGER
%token <f> FLOAT
%token <s> STRING
/* for VOUnits... */
%token <s> QUOTED_STRING
%token <l10f> VOUFLOAT
/* for CDS... */
%token <l10f> CDSFLOAT
/* tokens without values */
%token WHITESPACE
%token STARSTAR CARET
%token DIVISION
%token DOT STAR
%token PERCENT
%token OPEN_P CLOSE_P
/* for CDS... */
%token OPEN_SQ CLOSE_SQ
%token LIT10
/* For VOUnits... */
%token LIT1

/* Note: types are a Bison/byacc extension to yacc */
%type <f> numeric_power
@+!cds
%type <f> parenthesized_number
@-
%type <u> term unit
%type <i> integer
%type <uList> product_of_units unit_expression complete_expression function_application
%type <f> scalefactor
/* function_operand appears only in the VOUnits case, and is of expression type */
@=vounits  %type <uExpr> function_operand

/* the following two lines are really @bison, since they use bison-specific extension
   -- byacc doesn't understand it */
/*
@=c %error-verbose
@=c %debug
*/

%%
input: complete_expression {
            @!input:complete-expression
          }
        | scalefactor complete_expression {
            @!input:sf-complete-expression
        }
@+vounits
        | LIT1 {
            @!input:dimensionless
          }
@-vounits
@+ogip|fits
// Factors with preceding spaces: OK in OGIP (by implication from the examples),
// OK in FITS (not explicitly permitted or forbidden, and none of the examples include it,
// but it seems reasonable to permit it here)
        | scalefactor WHITESPACE complete_expression {
            @!input:sf-ws-complete-expression
          }
@-ogip|fits
@+fits
// The FITS spec may or may not be intended to permit "10+3 /m",
// but we don't (because ... for heavens' sake!)
        | division unit_expression {
            @!input:division+unit-expression
          }
@-
        ;

complete_expression: product_of_units {
            @!ce-product-of-units
          }
@+fits|vounits
// The FITS spec isn't completely clear on the topic of
// solidi, saying "Parentheses are used for symbol grouping and are
// strongly recommended whenever the order of operations might be
// subject to misinterpretation" and "The IAU style manual forbids
// the use of more than one solidus (/) character in a units
// string. However, since normal mathematical precedence rules apply
// in this context, more than one solidus may be used but is
// discouraged" (p27).  Therefore, it's not clear whether, for
// example, "kg/m s" should be parsed as "kg m-1 s-1", as "kg m-1 s", or
// forbidden on the grounds that 'normal mathematical precedence
// rules' would forbid it (it's probably arguable all ways, but I
// don't think that 'normal mathematical precedence rules' are going
// to resolve it).  Here, we resolve this by declaring that there can
// be only a single expression to the right of the solidus.  That is,
// we do not have "product_of_units DIVISION product_of_units" here.
//
// Note: it is I think a consequence of this that nothing can be
// successully parsed in two different grammars, with different
// meanings.  If the right-hand-side of the division could be a
// product_of_units, then "kg /m s" would parse in both FITS and OGIP,
// but mean "kg m-1 s-1" in FITS and "kg m-1 s" in OGIP.
        | product_of_units division unit_expression {
            @!ce-product-with-division
          }
@-
        ;

@+fits|vounits
product_of_units: unit_expression {
            $$ = $1;
          }
        | product_of_units product unit_expression {
            @!product-of-units+product+unit-expression
          }
        ;
@-fits|vounits
@+ogip|cds
// We conceive of the product_of_units as a sequence of terms 'times
// expression' or 'dividedby expression', multiplying them together
// after, in the latter case, reciprocating them.
product_of_units: unit_expression
        | division unit_expression {
            @!division+unit-expression
          }
        | product_of_units product unit_expression {
            @!product-of-units+product+unit-expression
          }
        | product_of_units division unit_expression {
            @!product-of-units+division+unit-expression
          }
        ;
@-ogip|cds

unit_expression: term {
            @!unit
          }
@+fits
        // m(2) is m^2, not function application
        | STRING parenthesized_number {
            @!string+numeric-power
          }
@-
        | function_application {
            $$ = $1;
          }
        | OPEN_P complete_expression CLOSE_P {
            $$ = $2;
          }
        ;

@+!(cds|vounits)
function_application: STRING OPEN_P complete_expression CLOSE_P {
            @!function-application
          };
@-
@+vounits
function_application: STRING OPEN_P {
@=java                                lexer.yybegin(Yylex.vouinitial);
//                                    The following is BEGIN(vouinitial),
//                                    but BEGIN is defined internally to the generated lexer,
//                                    and isn't exposed, so...
//                                    we have to use the force_lexer_state function
//                                    that the Makefile smuggled into unity-lexer.c
//                                    ... YUK (no, I don't like this any more than you do)
@=c                                   force_lexer_state(lexer_vouinitial);
                                    } function_operand CLOSE_P {
            @!function-application:vounits
          };
function_operand: complete_expression {
            @!function-operand:vounits
          }
        | scalefactor complete_expression {
            @!function-operand:vounits+scalefactor
        };
@-vounits
@+vounitsXX
        | QUOTED_STRING OPEN_P complete_expression CLOSE_P {
            @!quoted-function-application
          }
@-vounitsXX
@+cds
function_application: OPEN_SQ complete_expression CLOSE_SQ {
            @!cds-log
          }
        ;
@-

scalefactor: LIT10 power numeric_power { // eg 10^3
@=c                   $$ = $3;
@=java                $$ = Double.valueOf($3);
                  }
@+fits|cds
        | LIT10 SIGNED_INTEGER { // eg 10+3
            @!factor:integer+integer
          }
@-
@+cds
        | UNSIGNED_INTEGER {
            @!factor:cdsinteger
          }
@-
@+cds|ogip|vounits
        | LIT10 {
            $$ = 1;  /* log(10) = 1 */
          }
@-
@+vounits
        | LIT1 {
            $$ = 0;  /* log(1) = 0 */
          }
        | VOUFLOAT {
            @!factor:logfloat
          }
@-
@+cds
// The scalefactor must be positive.
// (an extra-syntactic comstraint)
        | CDSFLOAT { // eg 1.5x10+11
            @!factor:logfloat
          }
        | FLOAT {
            @!factor:float
          }
@-
@+ogip
// OGIP requires these scalefactors to be powers of 10,
// (an extra-syntactic comstraint)
        | FLOAT {
            @!factor:float10
          }
@-
        ;

@+ogip
// OGIP recommends no whitespace after the slash
division: DIVISION | WHITESPACE DIVISION
        | WHITESPACE DIVISION WHITESPACE | DIVISION WHITESPACE;
@-
@=!ogip division: DIVISION;

term: unit { $$ = $1; }
@+fits|cds
        | unit numeric_power {
            @!power-of-unit2
          }
@-
@+!cds
        | unit power numeric_power {
            @!power-of-unit3
          }
@-
        ;

unit: STRING {
            @!one-unit
          }
@+vounits
        | QUOTED_STRING {
            @!quoted-string
          }
        | STRING QUOTED_STRING {
            @!prefixed-quoted-string
          }
@-vounits
@+cds|vounits
        | PERCENT {
            @!percent
          }
@-
        ;

@+fits
power: CARET
        | STARSTAR
        ;
@-fits
@+vounits
power: STARSTAR;
@-
@+cds|ogip
power: STARSTAR;
@-

@=ogip numeric_power: UNSIGNED_INTEGER { $$ = $1; }
@=!ogip numeric_power: integer { $$ = $1; }
@+ogip
// OGIP, uniquely, apparently permits str1**y where y can also be a float
// (though none of its examples include this).
// m**3/2 would appear to be OK, too,
// but I think that would be misparsed, so forbid it here
        | FLOAT {
            if ($1 < 0) {
                yyerror("Can't have bare negative power in exponent");
@=c             YYERROR;
            }
            $$ = $1;
          }
@-ogip
@+!cds
        | parenthesized_number { $$ = $1; }
@-
        ;

@+!cds
parenthesized_number: OPEN_P integer CLOSE_P {
            $$ = $2;
          }
        | OPEN_P FLOAT CLOSE_P {
            $$ = $2;
          }
        | OPEN_P integer division UNSIGNED_INTEGER CLOSE_P {
            // This is certainly OK for FITS, not clear for OGIP
            $$ = ((float)$2)/((float)($4));
          }
        ;
@-!cds

integer: SIGNED_INTEGER | UNSIGNED_INTEGER;

@=cds   product: DOT;
@=fits  product: WHITESPACE | STAR | DOT;
@=vounits   product: DOT;
@+ogip
product: WHITESPACE | STAR | WHITESPACE STAR
       | WHITESPACE STAR WHITESPACE | STAR WHITESPACE;
@-ogip
%%

@!user-code
