/**
 * @file unity.h
 * The Library to support parsing unit strings
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <setjmp.h>

#define UNITY_INTERNAL 1
#include "unitylib.h"           /* functions in this module */
#include "unit-definitions.h"
#include "function-definitions.h"
#include "unity-constants.h"
#include "known-syntaxes.h"
#include "config.h"
#include "version.h"

// Declare functions defined elsewhere
int yyparse(void);              /* from yacc */
int lex_setup(const char* s, const UnitySyntax);   /* from lex.lex */
char lex_unexpected_character(void);

void yyerror(const char *s);

static const char* find_prefix_and_base(const char* s,
                                        int* pfx,
                                        const UnitySyntax syntax,
                                        int* is_base10_p);
static char* u_copy_string(const char* s);

#define TRUE 1
#define FALSE 0


// Functions defined in this module
// Functions u_* are 'internal' to the library.
// Some of these are additionally static.

/*
 * u_new_unit and u_function_application allocate new Unit objects.
 * If the parse succeeds,
 * these are subsequently owned and managed by a UnitExpression.  If
 * it does not succeed, however, then these aren't managed by
 * anything.  Keep track of them, therefore in a list within
 * track_unit.  See unity_parse_string, which manages them after an
 * unsuccessful parse.
 */
static const Unit** unit_tracking = NULL;
static int unit_tracking_size = 0;
static int unit_tracking_live = 0;
// (unit_tracking != NULL || unit_tracking_live == 0) initially true
static void track_unit(const Unit* u)
{
    if (u == NULL) {
        // all's well (presumably), so stop tracking these units
        // (we don't free them, because they're now owned by the
        // UnitExpression they're part of, so we simply forget about them)
        unit_tracking_live = 0;
    } else {
        if (unit_tracking_live >= unit_tracking_size) {
            if (unit_tracking != NULL) {
                unit_tracking_size *= 2;
                unit_tracking = (const Unit**)realloc((void*)unit_tracking, unit_tracking_size*sizeof(Unit*));
            } else {
                // first time
                unit_tracking_size = 8; /* initial size */
                unit_tracking = (const Unit**)malloc(unit_tracking_size*sizeof(Unit*));
            }
        }
        unit_tracking[unit_tracking_live++] = u;
    }
    assert(unit_tracking != NULL || unit_tracking_live == 0);
    return;
}
/* Free all of the tracked units */
static void track_unit_free(void)
{
    int i;
    assert(unit_tracking != NULL || unit_tracking_live == 0);
    for (i=0; i<unit_tracking_live; i++) {
        u_free_unit(unit_tracking[i]);
    }
    unit_tracking_live = 0;
    return;
}
/* Is the tracking system empty? (for assertions) */
#define TRACK_UNIT_EMPTY_P (unit_tracking_live == 0)

Unit* u_new_unit(const char* unit_string, const float power, const UnitySyntax syntax)
{
    const UnitDef* def;
    Unit* rval;

    if ((rval = (Unit*)malloc(sizeof(Unit))) == NULL) {
        fprintf(stderr, "Unable to allocate new unit\n");
        exit(1);
    }
    assert(rval != NULL);
    track_unit(rval);

    rval->power = power;            /* easy */
    rval->type = simple_unit_type;
    rval->is_quoted_unit = FALSE;
    rval->next = NULL;

    /* First of all, check if the complete string matches as a unit.
       This is what ensures that ‘Pa’ is parsed as ‘pascal’ and not
       ‘peta-annum’. */
    if ((def = unity_get_unitdef_from_string(unit_string, syntax)) != NULL) {
        rval->u.prefix_power = 0;
        rval->u.base_unit_def = def;
        rval->u.base_unit_string = NULL;
        rval->u.base10_p = 1;
    } else {
        const char* base_unit;
        int prefix_power;
        int is_base10_p = 1;

        if ((base_unit = find_prefix_and_base(unit_string,
                                              &prefix_power,
                                              syntax,
                                              &is_base10_p)) != NULL) {
            rval->u.prefix_power = prefix_power;
            if ((def = unity_get_unitdef_from_string(base_unit, syntax)) == NULL) {
                rval->u.base_unit_string = u_copy_string(base_unit);
                rval->u.base_unit_def = NULL;
            } else {
                rval->u.base_unit_string = NULL;
                rval->u.base_unit_def = def;
            }
        } else {
            rval->u.prefix_power = 0;
            rval->u.base_unit_string = u_copy_string(unit_string);
            rval->u.base_unit_def = NULL;
        }
        rval->u.base10_p = is_base10_p;
    }

    // structure invariant: precisely one of base_unit_string and base_unit_def is non-NULL
    assert(   (rval->u.base_unit_string == NULL && rval->u.base_unit_def != NULL)
           || (rval->u.base_unit_string != NULL && rval->u.base_unit_def == NULL));
    // not an invariant, but always true on creation
    assert(rval->next == NULL);

    return rval;
}

Unit* u_power_of_unit(Unit* u0, const float power)
{
    u0->power = power;
    return u0;
}

Unit* u_quoted_unit(const char* unitPrefix, /* unit prefix (eg 'k') or NULL */
                    const char* unitString, /* unit string (eg 'furlong') */
                    const float power,
                    const UnitySyntax syntax)
{
    // very similar to u_new_unit
    Unit* rval;

    if ((rval = (Unit*)malloc(sizeof(Unit))) == NULL) {
        fprintf(stderr, "Unable to allocate new unit\n");
        exit(1);
    }
    assert(rval != NULL);
    track_unit(rval);

    rval->power = power;            /* easy */
    rval->type = simple_unit_type;
    rval->is_quoted_unit = TRUE;
    rval->next = NULL;

    rval->u.base_unit_string = u_copy_string(unitString);
    rval->u.base_unit_def = NULL;
    if (unitPrefix == NULL) {
        // no prefix
        rval->u.prefix_power = 0;
        rval->u.base10_p = TRUE;
    } else {
#define BUFLEN 4
        char pfxbuf[BUFLEN];
        int pfxlen = strlen(unitPrefix);
        // pfxlen can be either 1 or 2
        const char* base;
        int isbase10_p;
        int power;

        switch(pfxlen) {
          case 0:
            yyerror("Impossible zero-length prefix before quoted unit");
            break;

          case 1:
          case 2:
            // add a fake unit "X", so that find_prefix_and_base will
            // not object to the lack of a unit
            snprintf(pfxbuf, BUFLEN, "%sX", unitPrefix);
            break;

          default:
            yyerror("Impossible prefix before quoted unit");
            break;
        }
        base = find_prefix_and_base(pfxbuf,
                                    &power,
                                    syntax,
                                    &isbase10_p);
        if (base != NULL) {
            rval->u.prefix_power = power;
            rval->u.base10_p = isbase10_p;
        } else {
            yyerror("Impossible prefix before quoted unit");
        }
#undef BUFLEN
    }

    // structure invariant: precisely one of base_unit_string and
    // base_unit_def is non-NULL
    // (same structure invariant as u_new_unit)
    assert(   (rval->u.base_unit_string == NULL && rval->u.base_unit_def != NULL)
              || (rval->u.base_unit_string != NULL && rval->u.base_unit_def == NULL));
    // not an invariant, but always true on creation
    assert(rval->next == NULL);

    return rval;
}

Unit* u_function_application(const char* functionName,
                             const float log_factor,
                             const Unit* unit_sequence,
                             const UnitySyntax syntax)
{
    Unit* U;
    const FunctionDef* def;

    if ((U = (Unit*)malloc(sizeof(Unit))) == NULL) {
        fprintf(stderr, "Can't allocate new unit; bailing!\n");
        exit(1);
    }
    assert(U != NULL);
    track_unit(U);

    // easy ones first
    U->type = function_application_type;
    U->power = 1;
    U->is_quoted_unit = FALSE;
    U->next = NULL;

    if ((def = unity_get_functiondef_from_string(functionName, syntax)) == NULL) {
        U->f.definition = NULL;
        U->f.name = u_copy_string(functionName);
    } else {
        U->f.definition = def;
        U->f.name = NULL;
    }
    assert(   (U->f.name == NULL && U->f.definition != NULL)
           || (U->f.name != NULL && U->f.definition == NULL));
    U->f.operand.unit_sequence = unit_sequence;
    U->f.operand.log_factor = log_factor;
    U->f.operand.sorted_unit_sequence = NULL;

    // not an invariant, but always true on creation
    assert(U->next == NULL);

    return U;
}

/*
 * Frees a previously obtained unit.
 */
void u_free_unit(const Unit* u)
{
    assert(u != NULL);
    switch(u->type) {
      case simple_unit_type:
        if (u->u.base_unit_string != NULL) {
            free((void*)u->u.base_unit_string);
        }
        break;

      case function_application_type:
        if (u->f.name != NULL) {
            free((void*)u->f.name);
        }
        break;

      default:
        assert(0);
    }
    free((void*)u);
}

/**
 * Frees a previously obtained unit-expression
 */
void unity_free_expression(const UnitExpression* ue)
{
    const Unit* u;
    const Unit* next;

    assert(ue != NULL);
    for (u=ue->unit_sequence; u!=NULL; u=next) {
        next = u->next;
        u_free_unit(u);
    }
    free((void*)ue);
}

static int uu_compare_unit(const void* o1, const void* o2)
{
    Unit* u1 = *((Unit**)o1);
    Unit* u2 = *((Unit**)o2);

    int cmp;
    if (u1->type == u2->type) {
        // same type
        if (u1->power == u2->power) {
            if (u1->type == simple_unit_type) {
                if (u1->u.base_unit_def != NULL) {
                    if (u2->u.base_unit_def != NULL)
                        cmp = strcmp(u1->u.base_unit_def->uri, u2->u.base_unit_def->uri);
                    else
                        cmp = -1;
                } else {
                    if (u2->u.base_unit_def != NULL)
                        cmp = +1;
                    else {
                        assert(   u1->u.base_unit_string != NULL
                               && u2->u.base_unit_string != NULL);
                        cmp = strcmp(u1->u.base_unit_string, u2->u.base_unit_string);
                    }
                }
            } else {
                assert (u1->type == function_application_type);
                if (u1->f.definition != NULL)
                    if (u2->f.definition != NULL)
                        cmp = strcmp(u1->f.definition->description,
                                     u2->f.definition->description);
                    else
                        cmp = -1;
                else
                    if (u2->f.definition != NULL)
                        cmp = +1;
                    else {
                        assert(u1->f.name != NULL && u2->f.name != NULL);
                        cmp = strcmp(u1->f.name, u2->f.name);
                    }
                if (cmp == 0) {
                    // still -- compare operands
                    int len1, len2;
                    Unit** sus1 = u_get_sorted_unit_sequence(&(u1->f.operand), &len1);
                    Unit** sus2 = u_get_sorted_unit_sequence(&(u2->f.operand), &len2);
                    if (len1 == len2) {
                        int i;
                        for (i=0; i<len1; i++) {
                            cmp = uu_compare_unit(&sus1[i], &sus2[i]);
                            if (cmp != 0) {
                                break;
                            }
                        }
                    } else {
                        cmp = (len1 < len2 ? -1 : +1);
                    }
                }
            }
        } else {
            if (u1->power * u2->power < 0) {
                // exponents of opposite signs
                cmp = (u1->power > 0 ? -1 : +1); /* positive exponents order first */
            } else {
                // lower exponents order first
                cmp = (fabs(u1->power) < fabs(u2->power) ? -1 : +1);
            }
        }
    } else {
        // different types, so order simple units before function-application units
        cmp = (u1->type == simple_unit_type ? -1 : +1);
    }

    return cmp;
}

Unit** u_get_sorted_unit_sequence(UnitExpression* ue, int* len)
{
    // The only time ue->unit_sequence != NULL is when this is the dimensionless quantity
    if (ue->sorted_unit_sequence == NULL && ue->unit_sequence != NULL) {
        int n, i;
        const Unit* p;
        Unit **sus;

        n = 0;
        for (p=ue->unit_sequence; p != NULL; p=p->next) {
            n++;
        }
        if ((sus = (Unit**)malloc(n*sizeof(Unit*))) == NULL) {
            fprintf(stderr, "Unable to allocate sorted sequence\n");
            exit(1);
        }
        ue->sorted_unit_sequence = sus;
        ue->nunits = n;

        for (i=0, p=ue->unit_sequence; i<n; i++, p=p->next) {
            sus[i] = (Unit*)p;
        }

        if (n < 2) {
            // trivial array -- doesn't need sorted
        } else {
            qsort((void*)sus, n, sizeof(Unit*), uu_compare_unit);
        }
    }

    *len = ue->nunits;
    return ue->sorted_unit_sequence;
}

/**
 * Test whether two units are equal.
 * Returns false if either argument is NULL.
 * @return true (non-zero) if the two units are equal
 */
int unity_equal_unit_p(const Unit* u1, const Unit* u2)
{
    if (u1 == NULL || u2 == NULL) {
        return FALSE;
    } else {
#if 0
        int cmp = uu_compare_unit(&u1, &u2);
        fprintf(stderr,  "equal_unit_p: %s%g : %s%g -> %d\n",
                (u1->u.base_unit_def == NULL
                 ? u1->u.base_unit_string
                 : u1->u.base_unit_def->uri),
                u1->power,
                (u2->u.base_unit_def == NULL
                 ? u2->u.base_unit_string
                 : u2->u.base_unit_def->uri),
                u2->power,
                cmp);
        return cmp == 0;
#endif
        return uu_compare_unit(&u1, &u2) == 0;
    }
}

/**
 * Tests whether two unit expressions are equal.
 * Returns false if either argument is NULL.
 * @return true (non-zero) if the two expressions are equal
 */
int unity_equal_expression_p(const UnitExpression* ue1, const UnitExpression* ue2)
{
    Unit** ss1;
    Unit** ss2;
    int len1, len2, i;

    if (ue1 == NULL || ue2 == NULL)
        return FALSE;

    if (ue1->log_factor != ue2->log_factor) {
        // yes, compare these as floats
        // (OK, since these are generally round numbers or simple fractions)
        return FALSE;
    }

    ss1 = u_get_sorted_unit_sequence((UnitExpression*)ue1, &len1);
    ss2 = u_get_sorted_unit_sequence((UnitExpression*)ue2, &len2);

    if (len1 != len2) {
        return FALSE;
    }

    for (i=0; i<len1; i++) {
        if (! unity_equal_unit_p(ss1[i], ss2[i])) {
            return FALSE;
        }
    }

    return TRUE;
}

/**
 * Returns the i'th unit in the given expression.
 * @param ue a non-NULL unit expression
 * @param idx the index of the unit to be retrieved from the expression (zero-offset)
 * @return the unit at the given position, or NULL if i is out of range
 */
const Unit* unity_get_unit_by_index(const UnitExpression* ue, const int idx)
{
    int i;
    const Unit* p;

    if (ue == NULL || idx < 0 || ue->unit_sequence == NULL)
        return NULL;

    assert(ue->unit_sequence != NULL);

    p = ue->unit_sequence;
    for (i=0; TRUE; i++) {
        if (p == NULL)
            break;              /* index is out of range */
        if (i == idx)
            break;              /* found it */
        p = p->next;
    }
    return p;
}

/**
 * Retrieves the definition of a known unit.
 * Returns null if the unit is a function-application unit
 * @return the unitdef if this is a known unit, or NULL if not
 * @see #unity_get_functiondef_from_unit
 */
const UnitDef* unity_get_unitdef_from_unit(const Unit* u)
{
    if (u == NULL)
        return NULL;
    else if (u->type != simple_unit_type)
        return NULL;
    else if (u->u.base_unit_def == NULL)
        return NULL;
    else
        return u->u.base_unit_def;
}

/**
 * Retrieves the definition of a known function-application unit.
 * Returns null if the unit is not a function-application unit
 * @return the functiondef if this is a known unit, or NULL if not
 * @see #unity_get_unitdef_from_unit
 */
const FunctionDef* unity_get_functiondef_from_unit(const Unit* u)
{
    if (u == NULL)
        return NULL;
    else if (u->type != function_application_type)
        return NULL;
    else if (u->f.definition == NULL)
        return NULL;
    else
        return u->f.definition;
}

/*
 * Divide the numerator by the denominator.
 * Nothing fancy -- just flip the powers of the denominator and append it to the numerator.
 * If either of the arguments is NULL, return NULL.
 * Modifies both.
 */
Unit* u_divide_units(Unit* num, Unit* den)
{
    Unit* p;

    if (num == NULL || den == NULL) {
        return NULL;
    }
    for (p=den; p!=NULL; p=p->next) {
        p->power *= -1;
    }
    // find the end of the numerator string
    for (p=num; p->next != NULL; p=p->next) {
        // do nothing
    }
    p->next = den;
    return num;
}

Unit* u_unit_reciprocal(Unit* u)
{
    Unit* p;

    assert(u != NULL);
    for (p=u; p!=NULL; p=p->next) {
        p->power *= -1;
    }
    return u;
}

Unit* u_unit_append(Unit* u1, const Unit* u2)
{
    Unit* p;

    assert(u1 != NULL && u2 != NULL);
    for (p=u1; p->next!=NULL; p=p->next) {
        // nothing
    }
    p->next = (Unit*)u2;
    return u1;
}

/*
 * Return a pointer to a copy of the given non-null string.
 * The pointer must be freed by the caller.
 */
static char* u_copy_string(const char* s)
{
    int len;

    assert(s != NULL);          /* programming error if s is NULL */
    len = strlen(s);
    char* rval;
    if ((rval = (char*)malloc(len+1)) == NULL) {
        fprintf(stderr, "Can't malloc %d bytes for copy_string\n", len+1);
        exit(1);
    }
    memcpy(rval, s, len+1);
    return rval;
}

/*
 * Translate the character prefix to the base-10 log it corresponds to.
 * @return a non-zero integer.
 */
int u_prefix_to_power10(const char pfx)
{
    int rval = 0; /* initialising keeps the compiler happy, and allows the assertion check at the end */
    switch (pfx) {
      case 'q': rval = -30; break;
      case 'r': rval = -27; break;
      case 'y': rval = -24; break;
      case 'z': rval = -21; break;
      case 'a': rval = -18; break;
      case 'f': rval = -15; break;
      case 'p': rval = -12; break;
      case 'n': rval = -9; break;
      case 'u': rval = -6; break;
      case 'm': rval = -3; break;
      case 'c': rval = -2; break;
      case 'd': rval = -1; break;
      case UNITY_PREFIX_DECA:
        rval = 1; break; /* deca */
      case 'h': rval = 2; break;
      case 'k': rval = 3; break;
      case 'M': rval = 6; break;
      case 'G': rval = 9; break;
      case 'T': rval = 12; break;
      case 'P': rval = 15; break;
      case 'E': rval = 18; break;
      case 'Z': rval = 21; break;
      case 'Y': rval = 24; break;
      case 'R': rval = 27; break;
      case 'Q': rval = 30; break;
      default:
        // programming error -- this function shouldn't have been called
        fprintf(stderr, "Impossible prefix: <%c>\n", pfx);
        assert(0);
        break;
    }
    assert(rval != 0);
    return rval;
}

/*
 * Translate the character prefix to the base-2 log it corresponds to.
 * @return a positive integer.
 */
int u_prefix_to_power2(const char pfx)
{
    int rval = 0;
    switch (pfx) {
      case 'K': rval = 10; break;
      case 'M': rval = 20; break;
      case 'G': rval = 30; break;
      case 'T': rval = 40; break;
      case 'P': rval = 50; break;
      case 'E': rval = 60; break;
      case 'Z': rval = 70; break;
      case 'Y': rval = 80; break;
      default:
        // programming error -- this function shouldn't have been called
        fprintf(stderr, "Impossible prefix: %ci\n", pfx);
        assert(0);
        break;
    }
    assert(rval != 0);
    return rval;
}

typedef int (*parsef_ptr)(void);
int fits_yyparse(void);
int ogip_yyparse(void);
int cds_yyparse(void);
int vounits_yyparse(void);
static struct {
    const char* name;
    const UnitySyntax stx;
    const parsef_ptr parser;
} parser_list[] = {
    { "fits",    UNITY_SYNTAX_FITS,    &fits_yyparse },
    { "ogip",    UNITY_SYNTAX_OGIP,    &ogip_yyparse },
    { "cds",     UNITY_SYNTAX_CDS,     &cds_yyparse },
    { "vounits", UNITY_SYNTAX_VOUNITS, &vounits_yyparse },
    { "latex",   UNITY_SYNTAX_LATEX,   NULL },
    { "debug",   UNITY_SYNTAX_DEBUG,   NULL },
};
static int parser_list_size = sizeof(parser_list) / sizeof(parser_list[0]);

static parsef_ptr get_parser_for_syntax(const UnitySyntax syntax)
{
    int i;
    parsef_ptr rval;

    for (i=0; i<parser_list_size; i++) {
        if (syntax == parser_list[i].stx) {
            rval = parser_list[i].parser;
            break;
        }
    }
    // if the following is not true, then the 'syntax' was not in
    // the parser_list[] array, so this file is out of sync with
    // the file known_syntaxes.h -- programming error
    assert(i < parser_list_size);

    return rval;
}

/**
 * Return the available parser names.
 * The returned list is statically allocated, and should not be freed
 * by the caller.
 * @return a NULL-terminated list of pointers to parser names
 */
const char** unity_parser_names(void)
{
    static const char** res = NULL;
    if (res == NULL) {
        // initialise
        int i;
        if ((res = (const char**)malloc(sizeof(char*)*(parser_list_size+1))) == NULL) {
            fprintf(stderr, "Can't allocate %ld bytes for unity_parser_names\n", sizeof(char*)*(parser_list_size+1));
            exit(1);
        }
        assert(res != NULL);
        for (i=0; i<parser_list_size; i++) {
            res[i] = parser_list[i].name;
        }
        res[parser_list_size] = NULL; /* terminate the list */
    }
    return res;
}

/**
 * Obtain the parser ID from a parser name.
 * The recognised parser names are those returned by {@link #unity_parser_names},
 * and the returned IDs can be given as arguments to {@link #unity_parse_string}.
 *
 * <p>An identifier for the returned parser identifier, as a {@link UnitySyntax}.
 * @param parser_name a name such as "fits"
 * @return returns {@link UNITY_SYNTAX_NONE} if the name is not recognised
 */
UnitySyntax unity_identify_parser(const char* parser_name)
{
    int i;
    UnitySyntax rval = UNITY_SYNTAX_NONE;

    for (i=0; i<parser_list_size; i++) {
        if (strcmp(parser_name, parser_list[i].name) == 0) {
            rval = parser_list[i].stx;
            break;
        }
    }
    // if the following is not true, then the 'syntax' was not in
    // the parser_list[] array, so this file is out of sync with
    // the file known_syntaxes.h -- programming error
    assert(i < parser_list_size);

    return rval;
}

/**
 * Finds a parser name from an ID
 * @param stx one of the members of the {@link UnitySyntax} enumeration
 * @return a name for the parser, which will not be NULL
 */
const char* unity_parser_name(const UnitySyntax stx)
{
    int i;
    const char* rval = NULL;

    for (i=0; i<parser_list_size; i++) {
        if (stx == parser_list[i].stx) {
            rval = parser_list[i].name;
            break;
        }
    }
    // if the following is not true, then the 'syntax' was not in
    // the parser_list[] array, so this file is out of sync with
    // the file known_syntaxes.h -- programming error
    assert(rval != NULL);

    return rval;
}

/* The parser reports the parse result by calling u_receive_result
 * with the result of the parse.  This is reported to the caller by
 * unity_parse_string, which then sets parse_result back to NULL.
 * These two functions communicate via the parse_result variable,
 * which should be NULL before when unity_parse_string starts, up to
 * the point where  u_receive_result is called, and NULL
 * after unity_parse_string finishes.
 *
 * This is obviously not thread-safe
 */
static UnitExpression* parse_result = NULL;
void u_receive_result(UnitExpression* result)
{
    // It's a consequence of the grammar that u_receive_result will be
    // called precisely once per successful parse (though zero or one
    // times in an unsuccessful parse)
    assert(parse_result == NULL);

    parse_result = result;

    assert(parse_result != NULL);
}

UnitExpression* u_new_expression(const float log_factor, const Unit* unit_sequence)
{
    UnitExpression* newexp;
    if ((newexp = (UnitExpression*)malloc(sizeof(UnitExpression))) == NULL) {
        fprintf(stderr, "unity: can't allocate %lu bytes for parse result!  Bailing...\n",
                (unsigned long)sizeof(UnitExpression));
        exit(1);
    }

    newexp->unit_sequence = unit_sequence;
    newexp->sorted_unit_sequence = NULL;
    if (unit_sequence == NULL) {
        newexp->log_factor = 0.0;
    } else {
        newexp->log_factor = log_factor;
    }

    // the only way that newexp->unit_sequence != NULL
    // will be if this is the dimensionless unit

    return newexp;
}

void unity_yyerror_jmp_set(jmp_buf *p);
void unity_yyerror_jmp_clear(void);

// Call the yyparse function, protected by setjmp.
// Function yyerror calls longjmp in this case.
static int do_parse(parsef_ptr yyp)
{
    jmp_buf env;
    int rval;

    if (setjmp(env) == 0) {
        // normal behaviour
        unity_yyerror_jmp_set(&env);
        rval = (*yyp)();
    } else {
        // there has been an error thrown by yyerror
        rval = 1;
    }
    unity_yyerror_jmp_clear();

    return rval;
}

/**
 * Parse a string.  If this returns with an error, then there should
 * be an error message available from {@link #unity_parse_error}.
 *
 * @param unit_string the string to be parsed
 * @param syntax the syntax to be used to parse the string
 * @return a parsed representation of the unit expression, or NULL on error
 */
const UnitExpression* unity_parse_string(const char* unit_string,
                                         const UnitySyntax syntax)
{
    parsef_ptr yyparse;
    const UnitExpression* rval;

    assert(parse_result == NULL);
    assert(TRACK_UNIT_EMPTY_P);

    if ((yyparse = get_parser_for_syntax(syntax)) != NULL) {
        lex_setup(unit_string, syntax);
        if (do_parse(yyparse) == 0 && !lex_unexpected_character()) {
            // successful parse
            rval = parse_result;
            // ensure that parse_result is clear, to make sure nothing else can get to it
            parse_result = NULL;
        } else {
            if (lex_unexpected_character()) {
                // this is a lexing error, not a parsing error
                char buf[64];           /* long enough? */
                snprintf(buf, 64, "Unexpected character '%c'", lex_unexpected_character());
                yyerror(buf);
            }
            /* parse error.  If there is a parse_result (that is,
             * u_receive_result was called, and what has happened is
             * that there was more in the parse string than was
             * matched by the 'begin' production), then free this and
             * the Unit objects it manages (which includes the tracked
             * units).  If there is not, then free each of the
             * (orphaned) Unit instances held in the unit_tracking
             * list.
             */
            if (parse_result == NULL) {
                track_unit_free();
            } else {
                unity_free_expression(parse_result);
                parse_result = NULL;
            }
            rval = NULL;
        }
        // in either case, stop tracking these units
        // (this simply causes the tracking to forget about these units, but mustn't free them)
        track_unit(NULL);
    } else {
        // we didn't even start to parse, so there are no leftover units to track
        char buf[64];
        snprintf(buf, 64, "Can't find parser for syntax: %d", syntax);
        yyerror(buf);
        rval = NULL;
    }

    // The following can be asserted in every case except when we have
    // parsed the dimensionless string ("1", in the VOUnits syntax):
    // in that single case (rval != NULL) but (rval->unit_sequence == NULL) also.
    //assert(rval == NULL || rval->unit_sequence != NULL);

    assert(parse_result == NULL);
    assert(TRACK_UNIT_EMPTY_P);

    return rval;
}

/**
 * Return an expression indicating a dimensionless quantity.
 *
 * <p>The return value is a pointer to a static constant object, and should not be freed.
 *
 * @return a pointer to a valid UnitExpression
 */
const UnitExpression* unity_get_dimensionless(void)
{
    static UnitExpression ue = { 0.0, NULL, NULL, 0 };
    return &ue;
}

/**
 * Examine the unit_string to see if there's an SI or binary prefix.  If there
 * is, return the base unit, after the prefix, and store into *pfx the
 * power corresponding to the unit.
 * Return a pointer to the beginning of the base unit.
 * This does not match if there is no unit following the prefix
 * (that is, if there's only a prefix).
 * If there is no prefix, return NULL.
 *
 * That is, this effectively matches the regexps "^(da|[qryzafpnumcdhkMGTPEZYRQ])(.+)"
 * and "^([KMGTPEZY]i)(.+)".
 *
 * Note that
 * <a href='https://www.bipm.org/en/cgpm-2022/resolution-3'>Resolution 3 of the 27th CGPM</a>
 * defines ronna/ronto/quetta/quecto, but doesn't mention the binary prefixes.
 */
static const char* find_prefix_and_base(const char* unit_string,
                                        int* pfx,
                                        const UnitySyntax syntax,
                                        int *is_base10_p)
{
    const char* prefixes10 = "qryzafpnumcdhkMGTPEZYRQ";
    const char* prefixes2 = "KMGTPEZY";
    const char* base_unit = NULL; /* NULL signals no match */
    const char char0 = unit_string[0];

    assert(char0 != '\0');      /* shouldn't be called in this case */

    if (strcmp(unit_string, "da") == 0) {
        // arcane special case -- this is a deci-year...
        *pfx = u_prefix_to_power10('d');
        base_unit = "a";
    } else if (strncmp(unit_string, "da", 2) == 0) {
        *pfx = u_prefix_to_power10(UNITY_PREFIX_DECA);
        base_unit = unit_string+2;
    } else if (unit_string[1] == 'i' && (strchr(prefixes2, char0) != NULL)) {
        // This looks very much like a binary prefix.
        // HOWEVER: we accept it as such _only_ if the base_unit which
        // we would return is a known unit which we permit to take
        // binary prefixes.
        // Yes, in this case we do end up calling unity_get_unitdef_from_string
        // twice; though inaesthetic, this will not boil the oceans.
        const UnitDef* def;
        def = unity_get_unitdef_from_string(unit_string+2, syntax);
        if (def == NULL) {
            // unknown unit, so no binary prefix should be recognised
            base_unit = NULL;
        } else {
            const UnitRep* rep = u_get_unit_representation(def, syntax);
            // I think that rep should be non-NULL, but haven't proved that
            if (rep != NULL && rep->binary_prefixes_p) {
                // it's OK -- recognise this as a binary prefix
                *pfx = u_prefix_to_power2(char0);
                *is_base10_p = FALSE;
                base_unit = unit_string + 2;
            } else {
                base_unit = NULL;
            }
        }
    }

    if (base_unit == NULL && strchr(prefixes10, char0) != NULL) {
        *pfx = u_prefix_to_power10(char0);
        *is_base10_p = TRUE;
        base_unit = unit_string + 1;
    }

    if (base_unit == NULL) {
        // no match for a prefix
        return NULL;
    } else if (*base_unit == '\0') {
        // there was a prefix, but it wasn't prefixing anything
        return NULL;
    } else {
        return base_unit;
    }
}

/**
 * Indicates whether the unit is being used in a way which satisfies
 * the indicated constraints in the gived syntax.  Units can be
 * <ol>
 * <li>‘known’, which means the (prose) definition of the syntax
 * recognises this unit and has something to say about it -- all the
 * syntaxes ‘know’ the metre;</li>
 * <li>‘deprecated’, which means that the definition acknowledges the
 * existence of the unit but advises against it -- for example the
 * ‘erg’ is either unknown or deprecated in this sense (below, we take
 * ‘recommended’ to be a synonym for ‘not deprecated’); or</li>
 * <li>‘unknown’, which means that the definition knows nothing about
 * this unit -- the ‘furlong’ tends not to be well-used in the
 * scientific literature.</li>
 * </ol>
 *
 * The checks are indicated by the bitwise OR of the following flags:
 *
 * <p><code>UNITY_CHECK_RECOGNISED</code>: the unit is a ‘known’ unit, in the
 * sense that it is listed in the specification of the corresponding
 * syntax.
 *
 * <p><code>UNITY_CHECK_RECOMMENDED</code>: the unit is a ‘known’ unit, and is
 * additionally not deprecated.
 *
 * <p><code>UNITY_CHECK_CONSTRAINTS</code>: the unit is being used in conformance
 * with any other constraints placed on it.  Most typically, it is either
 * allowed to have such a prefix, or it has no SI prefix.  Possibly
 * surprisingly, an ‘unknown’ unit has no (known) constraints, so it
 * satisfies all of them.
 *
 * <p>The constant <code>UNITY_CHECK_ALL</code> performs all checks.
 *
 * <p>The VOUnits syntax includes the notion of a ‘quoted’ unit (see
 * {@link unit_struct}).  Such a unit is always ‘unrecognised’.
 *
 * @param u the Unit to be checked
 * @param syntax the syntax whose rules are to be checked
 * @param flags the checks to be performed
 * @return true (non-zero) if the checks pass
 */
int unity_check_unit(const Unit* u, const UnitySyntax syntax, const int flags)
{
    const UnitRep* rep;
    int is_recognised;
    int is_recommended;
    int satisfies_constraints;
    int result;

    if (flags == 0) {
        // trivial check
        return TRUE;            /* JUMP OUT */
    }

    if (u->type == function_application_type) {
        const FunctionDef* fd = unity_get_functiondef_from_unit(u);
        if (fd == NULL && (flags & (UNITY_CHECK_RECOGNISED | UNITY_CHECK_RECOMMENDED))) {
            result = FALSE;
        } else {
            result = unity_check_expression(&(u->f.operand), syntax, flags);
        }
    } else {
        assert(u->type == simple_unit_type);

        rep = u_get_unit_representation(u->u.base_unit_def, syntax);
        if (rep == NULL) {
            is_recognised = FALSE;
            is_recommended = FALSE;
            satisfies_constraints = TRUE; /* no constraints, so all satisfied */
        } else {
            is_recognised = TRUE;
            is_recommended = !rep->is_deprecated_p;
            if (u->u.prefix_power == 0) {
                satisfies_constraints = TRUE; /* nothing to check */
            } else if (u->u.base10_p) {
                satisfies_constraints = rep->si_prefixes_p;
            } else {
                satisfies_constraints = rep->binary_prefixes_p;
            }
        }

        result = TRUE;
        if (flags & UNITY_CHECK_RECOGNISED)
            result &= is_recognised;
        if (flags & UNITY_CHECK_RECOMMENDED)
            result &= is_recommended;
        if (flags & UNITY_CHECK_CONSTRAINTS)
            result &= satisfies_constraints;
    }

    return result;
}

/**
 * Indicates whether the units in the expression are being used in a way which satisfies
 * the indicated constraints.  This checks each unit using
 * {@link #unity_check_unit}.
 *
 * @param ue the UnitExpression to be checked
 * @param syntax the syntax whose rules are to be checked
 * @param flags the checks to be performed
 * @return true (non-zero) if the checks pass
 */
int unity_check_expression(const UnitExpression* ue, const UnitySyntax syntax, const int flags)
{
    const Unit* u;
    for (u=ue->unit_sequence; u!=NULL; u=u->next) {
        if (! unity_check_unit(u, syntax, flags)) {
            return FALSE;
        }
    }
    return TRUE;
}

// Define yyerror.
// If yyerror is called in a dynamic extent between calls to
// unity_yyerror_jmp_set and unity_yyerror_jmp_clear then it will do a
// longjmp to the given environment.  Otherwise, it will return quietly.
static jmp_buf* yyerror_jmp_buf = NULL;
void unity_yyerror_jmp_set(jmp_buf *p)
{
    yyerror_jmp_buf = p;
}
void unity_yyerror_jmp_clear(void)
{
    yyerror_jmp_buf = NULL;
}

void lex_release(void);         /* defined in lex.lex */

static char* error_string = 0;
static int error_buflen = 0;
#define ERROR_STRING_PREAMBLE "units parsing error: "
void yyerror(const char *s)
{
    int length_needed  = strlen(ERROR_STRING_PREAMBLE) + strlen(s) + 1;

    lex_release();

    if (error_buflen == 0) {
        error_buflen = 2 * length_needed;
        error_string = (char*)malloc(error_buflen);
    } else if (error_buflen < length_needed) {
        error_buflen = 2 * length_needed;
        error_string = (char*)realloc((void*)error_string, error_buflen);
    } else {
        assert(error_string != NULL);
    }
    if (error_string == NULL) {
        fprintf(stderr, "Fatal error: couldn't allocate %d bytes for unity error message!\n", error_buflen);
        exit(1);                /* JUMP OUT -- too drastic? */
    }
    assert(error_string != NULL);
    assert(error_buflen >= length_needed);

    snprintf(error_string, error_buflen, "%s%s", ERROR_STRING_PREAMBLE, s);

    if (yyerror_jmp_buf != NULL) {
        longjmp(*yyerror_jmp_buf, 1);
    }
}
const char* unity_parse_error(void)
{
#if 0
    /* This is probably not useful behaviour
       (but if we subsequently decide it is, this is where it should go) */
    if (invalid_character != '\0') {
        /* this is a lexing error, not a parsing error */
        char buf[64];           /* long enough? */
        snprintf(buf, 64, "Unexpected character '%c'", invalid_character);
        yyerror(buf);
    }
#endif
    return (const char*)error_string;
}


/**
 * Return a version string for the library.
 * The form of the string is unspecified, but is intended to be printable
 * @see unity_version_number
 */
const char* unity_version_string(void)
{
    return VERSIONIDENT ", released " VERSIONDATE;
}

/**
 * Return a number which indicates the version and release numbers of the library.
 * The number is formed by major_version * 1e6 + minor_version * 1e3 + release_number
 * @see unity_version_string
 */
int unity_version_number(void)
{
    return PACKAGE_VERSION_INTEGER;
}
