/*++

Copyright (c) 2016 Minoca Corp. All Rights Reserved

Module Name:

    verbose.c

Abstract:

    This module implements verbose and debug output for the YY parser generator.

Author:

    Evan Green 24-Apr-2016

Environment:

    Build

--*/

//
// ------------------------------------------------------------------- Includes
//

#include "yygenp.h"
#include <stdio.h>
#include <string.h>

//
// --------------------------------------------------------------------- Macros
//

#define YYGEN_PLURALIZE(_Value) (((_Value) == 1) ? "" : "s")

//
// ---------------------------------------------------------------- Definitions
//

//
// ------------------------------------------------------ Data Type Definitions
//

//
// ----------------------------------------------- Internal Function Prototypes
//

VOID
YypGraphState (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    );

VOID
YypGraphLookaheads (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_RULE_INDEX RuleIndex,
    PYY_VALUE LookaheadIndex
    );

VOID
YypPrintGrammar (
    PYYGEN_CONTEXT Context,
    FILE *File
    );

VOID
YypPrintState (
    PYYGEN_CONTEXT Context,
    FILE *File,
    PYY_RULE_INDEX NullRules,
    YY_STATE_INDEX StateIndex
    );

VOID
YypPrintUnusedRules (
    PYYGEN_CONTEXT Context,
    FILE *File
    );

VOID
YypPrintConflicts (
    PYYGEN_CONTEXT Context,
    FILE *File
    );

VOID
YypPrintConflictsForState (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    );

VOID
YypPrintStateItemSets (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    );

VOID
YypPrintStateNulls (
    PYYGEN_CONTEXT Context,
    FILE *File,
    PYY_RULE_INDEX NullRules,
    YY_STATE_INDEX StateIndex
    );

VOID
YypPrintActions (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    );

VOID
YypPrintShifts (
    PYYGEN_CONTEXT Context,
    FILE *File,
    PYYGEN_ACTION Action
    );

VOID
YypPrintReductions (
    PYYGEN_CONTEXT Context,
    FILE *File,
    PYYGEN_ACTION Action,
    YY_RULE_INDEX DefaultReduction
    );

VOID
YypPrintGotos (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    );

//
// -------------------------------------------------------------------- Globals
//

//
// ------------------------------------------------------------------ Functions
//

VOID
YyPrintGraph (
    PYYGEN_CONTEXT Context,
    FILE *File
    )

/*++

Routine Description:

    This routine prints the state graph for the given parsed grammar.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

Return Value:

    None.

--*/

{

    YY_VALUE ShiftIndex;
    PYYGEN_SHIFTS Shifts;
    YY_STATE_INDEX ShiftState;
    YY_STATE_INDEX StateIndex;
    YY_VALUE Symbol;

    for (StateIndex = 0; StateIndex < Context->StateCount; StateIndex += 1) {
        YypEstablishClosure(Context, Context->StateTable[StateIndex]);
        YypGraphState(Context, File, StateIndex);
    }

    fprintf(File, "\n\n");
    for (StateIndex = 0; StateIndex < Context->StateCount; StateIndex += 1) {
        Shifts = Context->ShiftTable[StateIndex];
        if (Shifts != NULL) {
            for (ShiftIndex = 0; ShiftIndex < Shifts->Count; ShiftIndex += 1) {
                ShiftState = Shifts->States[ShiftIndex];
                Symbol = Context->AccessingSymbol[ShiftState];
                fprintf(File,
                        "\tq%d -> q%d [label=\"%s\"];\n",
                        StateIndex,
                        ShiftState,
                        Context->Elements[Symbol].Name);
            }
        }
    }

    fprintf(File, "}\n");
    return;
}

VOID
YyPrintParserState (
    PYYGEN_CONTEXT Context,
    FILE *File
    )

/*++

Routine Description:

    This routine prints a human readable description of the parser states.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

Return Value:

    None.

--*/

{

    PYY_RULE_INDEX NullRules;
    YY_STATE_INDEX StateIndex;

    NullRules = YypAllocate(Context->RuleCount * sizeof(YY_RULE_INDEX));
    if (NullRules == NULL) {
        goto DumpParserStateEnd;
    }

    YypPrintGrammar(Context, File);
    fprintf(File, "\n\n");
    for (StateIndex = 0; StateIndex < Context->StateCount; StateIndex += 1) {
        YypPrintState(Context, File, NullRules, StateIndex);
    }

    if (Context->UnusedRules != 0) {
        YypPrintUnusedRules(Context, File);
    }

    if ((Context->ShiftReduceConflictCount != 0) ||
        (Context->ReduceReduceConflictCount != 0)) {

        YypPrintConflicts(Context, File);
    }

    fprintf(File,
            "\n\n%d terminals, %d nonterminals\n",
            Context->TokenCount,
            Context->NonTerminalCount);

    fprintf(File,
            "%d grammar rules, %d states\n",
            Context->RuleCount - 2,
            Context->StateCount);

DumpParserStateEnd:
    if (NullRules != NULL) {
        YypFree(NullRules);
    }

    return;
}

//
// --------------------------------------------------------- Internal Functions
//

VOID
YypGraphState (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    )

/*++

Routine Description:

    This routine prints the graph for a particular state.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    StateIndex - Supplies the state to graph. The context's item set array
        should be properly set up for this state.

Return Value:

    None.

--*/

{

    PYY_VALUE Items;
    PYY_ITEM_INDEX ItemSet;
    PYY_VALUE ItemsStart;
    YY_VALUE LookaheadIndex;
    YY_RULE_INDEX RuleIndex;
    YY_VALUE Symbol;

    LookaheadIndex = Context->Lookaheads[StateIndex];
    fprintf(File, "\n\tq%d [label=\"%d:\\l", StateIndex, StateIndex);
    ItemSet = Context->ItemSet;
    while (ItemSet < Context->ItemSetEnd) {
        Items = Context->Items + *ItemSet;
        ItemsStart = Items;
        while (*Items >= 0) {
            Items += 1;
        }

        RuleIndex = -(*Items);
        Symbol = Context->Rules[RuleIndex].LeftSide;
        fprintf(File, "  %s -> ", Context->Elements[Symbol].Name);
        Items = Context->Items + Context->Rules[RuleIndex].RightSide;
        while (Items < ItemsStart) {
            fprintf(File, "%s ", Context->Elements[*Items].Name);
            Items += 1;
        }

        fputc('.', File);
        while (*Items >= 0) {
            fprintf(File, " %s", Context->Elements[*Items].Name);
            Items += 1;
        }

        if (*ItemsStart < 0) {
            YypGraphLookaheads(Context, File, RuleIndex, &LookaheadIndex);
        }

        fprintf(File, "\\l");
        ItemSet += 1;
    }

    fprintf(File, "\"]'");
    return;
}

VOID
YypGraphLookaheads (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_RULE_INDEX RuleIndex,
    PYY_VALUE LookaheadIndex
    )

/*++

Routine Description:

    This routine prints the lookahead tokens for a particular rule.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    RuleIndex - Supplies the rule whose lookaheads should be graphed.

    LookaheadIndex - Supplies a pointer to the index into the lookahead array.
        This may be updated.

Return Value:

    None.

--*/

{

    PULONG Row;
    YY_VALUE Token;
    ULONG TokenSetSize;

    TokenSetSize = YYGEN_BITMAP_WORD_COUNT(Context->TokenCount);
    if (RuleIndex == Context->LookaheadRule[*LookaheadIndex]) {
        Row = Context->LookaheadSets + (*LookaheadIndex * TokenSetSize);
        fprintf(File, " { ");
        for (Token = Context->TokenCount - 1; Token >= 0; Token -= 1) {
            if (YYGEN_BITMAP_IS_SET(Row, Token)) {
                fprintf(File, "%s ", Context->Elements[Token].Name);
            }
        }

        fprintf(File, " } ");
        *LookaheadIndex += 1;
    }

    return;
}

VOID
YypPrintGrammar (
    PYYGEN_CONTEXT Context,
    FILE *File
    )

/*++

Routine Description:

    This routine prints the set of numbered rules.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

Return Value:

    None.

--*/

{

    PYY_VALUE Item;
    YY_VALUE LeftSide;
    PSTR Name;
    PYYGEN_RULE Rule;
    YY_RULE_INDEX RuleIndex;
    INT Spacing;

    Spacing = 0;
    LeftSide = -1;
    for (RuleIndex = 2; RuleIndex < Context->RuleCount; RuleIndex += 1) {
        Rule = &(Context->Rules[RuleIndex]);
        if (Rule->LeftSide != LeftSide) {
            if (RuleIndex != 2) {
                fprintf(File, "\n");
            }

            Name = Context->Elements[Rule->LeftSide].Name;
            fprintf(File, "%4d  %s :", RuleIndex - 2, Name);
            Spacing = strlen(Name) + 1;

        } else {
            fprintf(File, "%4d  %*s|", RuleIndex - 2, Spacing, "");
        }

        Item = Context->Items + Rule->RightSide;
        while (*Item >= 0) {
            fprintf(File, " %s", Context->Elements[*Item].Name);
            Item += 1;
        }

        fprintf(File, "\n");
        LeftSide = Rule->LeftSide;
    }

    return;
}

VOID
YypPrintState (
    PYYGEN_CONTEXT Context,
    FILE *File,
    PYY_RULE_INDEX NullRules,
    YY_STATE_INDEX StateIndex
    )

/*++

Routine Description:

    This routine prints a human readable description of a particular parser
    state.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    NullRules - Supplies a pointer to a null rules array.

    StateIndex - Supplies the index of the state to print.

Return Value:

    None.

--*/

{

    if (StateIndex != 0) {
        fprintf(File, "\n\n");
    }

    if ((Context->ShiftReduceConflicts[StateIndex] != 0) ||
        (Context->ReduceReduceConflicts[StateIndex] != 0)) {

        YypPrintConflictsForState(Context, File, StateIndex);
    }

    fprintf(File, "state %d\n", StateIndex);
    YypPrintStateItemSets(Context, File, StateIndex);
    YypPrintStateNulls(Context, File, NullRules, StateIndex);
    YypPrintActions(Context, File, StateIndex);
    return;
}

VOID
YypPrintUnusedRules (
    PYYGEN_CONTEXT Context,
    FILE *File
    )

/*++

Routine Description:

    This routine prints a human readable description of the unreduced parser
    rules.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

Return Value:

    None.

--*/

{

    PYY_VALUE Items;
    PYYGEN_RULE Rule;
    YY_RULE_INDEX RuleIndex;

    fprintf(File, "\n\nRules never reduced:\n");
    for (RuleIndex = 3; RuleIndex < Context->RuleCount; RuleIndex += 1) {
        Rule = &(Context->Rules[RuleIndex]);
        if (Rule->Used == FALSE) {
            fprintf(File, "\t%s : ", Context->Elements[Rule->LeftSide].Name);
            Items = Context->Items + Rule->RightSide;
            while (*Items >= 0) {
                fprintf(File, " %s", Context->Elements[*Items].Name);
                Items += 1;
            }

            fprintf(File, "  (%d)\n", RuleIndex - 1);
        }
    }

    return;
}

VOID
YypPrintConflicts (
    PYYGEN_CONTEXT Context,
    FILE *File
    )

/*++

Routine Description:

    This routine prints a human readable description of the parser conflicts
    that could not be resolved via precedence or associativity rules.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

Return Value:

    None.

--*/

{

    ULONG ReduceConflicts;
    ULONG ShiftConflicts;
    YY_STATE_INDEX StateIndex;

    fprintf(File, "\n\n");
    for (StateIndex = 0; StateIndex < Context->StateCount; StateIndex += 1) {
        ShiftConflicts = Context->ShiftReduceConflicts[StateIndex];
        ReduceConflicts = Context->ReduceReduceConflicts[StateIndex];
        if ((ShiftConflicts != 0) || (ReduceConflicts != 0)) {
            fprintf(File, "State %d contains ", StateIndex);
            if (ShiftConflicts != 0) {
                fprintf(File,
                        "%d shift/reduce conflict%s",
                        ShiftConflicts,
                        YYGEN_PLURALIZE(ShiftConflicts));

                if (ReduceConflicts != 0) {
                    fprintf(File, ", ");
                }
            }

            if (ReduceConflicts != 0) {
                fprintf(File,
                        "%d reduce/reduce conflict%s",
                        ReduceConflicts,
                        YYGEN_PLURALIZE(ReduceConflicts));
            }

            fprintf(File, ".\n");
        }
    }

    return;
}

VOID
YypPrintConflictsForState (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    )

/*++

Routine Description:

    This routine prints a human readable description of the parser conflicts
    for a particular state.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    StateIndex - Supplies the state index to print conflicts for.

Return Value:

    None.

--*/

{

    PYYGEN_ACTION Action;
    PSTR ActionName;
    YY_ACTION_CODE Code;
    YY_ACTION_INDEX Number;
    YY_VALUE Symbol;

    Code = YyActionInvalid;
    Action = Context->Parser[StateIndex];
    Number = -1;
    Symbol = -1;
    while (Action != NULL) {
        if (Action->Suppression == YySuppressedQuietly) {
            Action = Action->Next;
            continue;
        }

        if (Action->Symbol != Symbol) {
            Symbol = Action->Symbol;
            Number = Action->Number;
            Code = Action->Code;
            if (Code == YyActionReduce) {
                Number -= 2;
            }

        } else if (Action->Suppression == YySuppressedNoisily) {
            if ((StateIndex == Context->FinalState) && (Symbol == 0)) {
                fprintf(File,
                        "%d: shift/reduce conflict (accept, reduce %d) on "
                        "$end\n",
                        StateIndex,
                        Action->Number - 2);

            } else {
                ActionName = "shift";
                if (Code == YyActionReduce) {
                    ActionName = "reduce";
                }

                fprintf(File,
                        "%d: %s/reduce conflict (%s %d, reduce %d) on %s\n",
                        StateIndex,
                        ActionName,
                        ActionName,
                        Number,
                        Action->Number - 2,
                        Context->Elements[Symbol].Name);
            }
        }

        Action = Action->Next;
    }

    return;
}

VOID
YypPrintStateItemSets (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    )

/*++

Routine Description:

    This routine prints the item sets in a particular state.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    StateIndex - Supplies the state index to print.

Return Value:

    None.

--*/

{

    PYY_VALUE Current;
    PYY_ELEMENT Elements;
    PYY_VALUE Items;
    YY_ITEM_INDEX ItemsCount;
    YY_ITEM_INDEX ItemSetIndex;
    PYYGEN_RULE Rule;
    YY_RULE_INDEX RuleIndex;
    PYYGEN_STATE State;

    Elements = Context->Elements;
    State = Context->StateTable[StateIndex];
    ItemsCount = State->ItemsCount;
    for (ItemSetIndex = 0; ItemSetIndex < ItemsCount; ItemSetIndex += 1) {
        Items = Context->Items + State->Items[ItemSetIndex];
        Current = Items;
        while (*Current >= 0) {
            Current += 1;
        }

        RuleIndex = -(*Current);
        Rule = &(Context->Rules[RuleIndex]);
        fprintf(File, "\t%s : ", Elements[Rule->LeftSide].Name);
        Current = Context->Items + Rule->RightSide;
        while (*Current >= 0) {
            if (Current == Items) {
                fprintf(File, ". ");
            }

            fprintf(File, "%s ", Elements[*Current].Name);
            Current += 1;
        }

        fprintf(File, "(%d)\n", RuleIndex - 2);
    }

    return;
}

VOID
YypPrintStateNulls (
    PYYGEN_CONTEXT Context,
    FILE *File,
    PYY_RULE_INDEX NullRules,
    YY_STATE_INDEX StateIndex
    )

/*++

Routine Description:

    This routine prints the item set states for empty rules.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    NullRules - Supplies a pointer to an array indexed by rule of whether or
        not a rule is null.

    StateIndex - Supplies the state index.

Return Value:

    None.

--*/

{

    PYYGEN_ACTION Action;
    YY_RULE_INDEX ActionIndex;
    YY_VALUE NullCount;
    YY_VALUE NullIndex;
    PYYGEN_RULE Rule;
    YY_RULE_INDEX RuleIndex;

    NullCount = 0;
    Action = Context->Parser[StateIndex];
    while (Action != NULL) {
        if ((Action->Code == YyActionReduce) &&
            (Action->Suppression != YySuppressedQuietly)) {

            ActionIndex = Action->Number;
            if (Context->Rules[ActionIndex].RightSide ==
                Context->Rules[ActionIndex + 1].RightSide) {

                RuleIndex = 0;
                while ((RuleIndex < NullCount) &&
                       (ActionIndex > NullRules[RuleIndex])) {

                    RuleIndex += 1;
                }

                if (RuleIndex == NullCount) {
                    NullCount += 1;
                    NullRules[RuleIndex] = ActionIndex;

                } else if (ActionIndex != NullRules[RuleIndex]) {
                    NullCount += 1;
                    for (NullIndex = NullCount - 1;
                         NullIndex > RuleIndex;
                         NullIndex -= 1) {

                        NullRules[NullIndex] = NullRules[NullIndex - 1];
                    }

                    NullRules[RuleIndex] = ActionIndex;
                }
            }
        }

        Action = Action->Next;
    }

    for (NullIndex = 0; NullIndex < NullCount; NullIndex += 1) {
        RuleIndex = NullRules[NullIndex];
        Rule = &(Context->Rules[RuleIndex]);
        fprintf(File,
                "\t%s : . (%d)\n",
                Context->Elements[Rule->LeftSide].Name,
                RuleIndex - 2);
    }

    fprintf(File, "\n");
    return;
}

VOID
YypPrintActions (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    )

/*++

Routine Description:

    This routine prints actions out of a particular state.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    StateIndex - Supplies the state index.

Return Value:

    None.

--*/

{

    PYYGEN_ACTION Action;
    PYYGEN_SHIFTS Shifts;
    YY_VALUE Symbol;

    if (StateIndex == Context->FinalState) {
        fprintf(File, "\t$end  accept\n");
    }

    Action = Context->Parser[StateIndex];
    if (Action != NULL) {
        YypPrintShifts(Context, File, Action);
        YypPrintReductions(Context,
                           File,
                           Action,
                           Context->DefaultReductions[StateIndex]);
    }

    Shifts = Context->ShiftTable[StateIndex];
    if ((Shifts != NULL) && (Shifts->Count != 0)) {
        Symbol = Context->AccessingSymbol[Shifts->States[Shifts->Count - 1]];
        if (Symbol >= Context->TokenCount) {
            YypPrintGotos(Context, File, StateIndex);
        }
    }

    return;
}

VOID
YypPrintShifts (
    PYYGEN_CONTEXT Context,
    FILE *File,
    PYYGEN_ACTION Action
    )

/*++

Routine Description:

    This routine prints the given shift actions.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    Action - Supplies a pointer to the head of the action list to print shifts
        for.

Return Value:

    None.

--*/

{

    while (Action != NULL) {
        if ((Action->Code == YyActionShift) &&
            (Action->Suppression == YyNotSuppressed)) {

            fprintf(File,
                    "\t%s  shift %d\n",
                    Context->Elements[Action->Symbol].Name,
                    Action->Number);
        }

        Action = Action->Next;
    }

    return;
}

VOID
YypPrintReductions (
    PYYGEN_CONTEXT Context,
    FILE *File,
    PYYGEN_ACTION Action,
    YY_RULE_INDEX DefaultReduction
    )

/*++

Routine Description:

    This routine prints the given reduction actions.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    Action - Supplies a pointer to the head of the action list to print
        reductions for.

    DefaultReduction - Supplies the default rule to reduce by.

Return Value:

    None.

--*/

{

    BOOL AnyReductions;
    YY_RULE_INDEX RuleIndex;

    AnyReductions = FALSE;
    while (Action != NULL) {
        if ((Action->Code == YyActionReduce) &&
            (Action->Suppression != YySuppressedQuietly)) {

            AnyReductions = TRUE;
        }

        if ((Action->Code == YyActionReduce) &&
            (Action->Number != DefaultReduction)) {

            RuleIndex = Action->Number - 2;
            if (Action->Suppression == YyNotSuppressed) {
                fprintf(File,
                        "\t%s  reduce %d\n",
                        Context->Elements[Action->Symbol].Name,
                        RuleIndex);
            }
        }

        Action = Action->Next;
    }

    if (DefaultReduction > 0) {
        fprintf(File, "\t.  reduce %d\n", DefaultReduction - 2);
    }

    if (AnyReductions == FALSE) {
        fprintf(File, "\t.  error\n");
    }

    return;
}

VOID
YypPrintGotos (
    PYYGEN_CONTEXT Context,
    FILE *File,
    YY_STATE_INDEX StateIndex
    )

/*++

Routine Description:

    This routine prints the gotos for the given action.

Arguments:

    Context - Supplies a pointer to the initialized grammar context.

    File - Supplies the file handle to output to.

    StateIndex - Supplies the state index to print gotos for.

Return Value:

    None.

--*/

{

    YY_STATE_INDEX Destination;
    YY_VALUE ShiftIndex;
    PYYGEN_SHIFTS Shifts;
    PYY_STATE_INDEX States;
    YY_VALUE Symbol;

    fputc('\n', File);
    Shifts = Context->ShiftTable[StateIndex];
    States = Shifts->States;
    for (ShiftIndex = 0; ShiftIndex < Shifts->Count; ShiftIndex += 1) {
        Destination = States[ShiftIndex];
        Symbol = Context->AccessingSymbol[Destination];
        if (Symbol >= Context->TokenCount) {
            fprintf(File,
                    "\t%s  goto %d\n",
                    Context->Elements[Symbol].Name,
                    Destination);
        }
    }

    return;
}

