calculator.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 09 Feb 2010 01:52:08 -0500
branchcalculator-experiment
changeset 826 59a6a8aa7d87
parent 825 1ec9b7cc9434
permissions -rw-r--r--
Don't pass the TokenData back out of the lemon code.

#define __MOJOSHADER_INTERNAL__ 1
#include "mojoshader_internal.h"

#if DEBUG_COMPILER_PARSER
#define LEMON_SUPPORT_TRACING 1
#endif

typedef union TokenData
{
    int64 i64;
    double dbl;
    const char *string;
} TokenData;

typedef struct StringBucket
{
    char *string;
    struct StringBucket *next;
} StringBucket;

typedef struct Context
{
    int isfail;
    int out_of_memory;
    MOJOSHADER_malloc malloc;
    MOJOSHADER_free free;
    void *malloc_data;
    int error_count;
    ErrorList *errors;
    Preprocessor *preprocessor;
    StringBucket *string_hashtable[256];
} Context;


// Convenience functions for allocators...

static inline void out_of_memory(Context *ctx)
{
    ctx->isfail = ctx->out_of_memory = 1;
} // out_of_memory

static inline void *Malloc(Context *ctx, const size_t len)
{
    void *retval = ctx->malloc((int) len, ctx->malloc_data);
    if (retval == NULL)
        out_of_memory(ctx);
    return retval;
} // Malloc

static inline char *StrDup(Context *ctx, const char *str)
{
    char *retval = (char *) Malloc(ctx, strlen(str) + 1);
    if (retval != NULL)
        strcpy(retval, str);
    return retval;
} // StrDup

static inline void Free(Context *ctx, void *ptr)
{
    if (ptr != NULL)  // check for NULL in case of dumb free() impl.
        ctx->free(ptr, ctx->malloc_data);
} // Free

typedef enum Operator
{
    OP_START_RANGE_UNARY,
    OP_POSTINCREMENT,
    OP_POSTDECREMENT,
    OP_PREINCREMENT,
    OP_PREDECREMENT,
    OP_NEGATE,
    OP_COMPLEMENT,
    OP_NOT,
    OP_END_RANGE_UNARY,

    OP_START_RANGE_BINARY,
    OP_DEREF_ARRAY,
    OP_CALLFUNC,
    OP_DEREF_STRUCT,
    OP_COMMA,
    OP_MULTIPLY,
    OP_DIVIDE,
    OP_MODULO,
    OP_ADD,
    OP_SUBTRACT,
    OP_LSHIFT,
    OP_RSHIFT,
    OP_LESSTHAN,
    OP_GREATERTHAN,
    OP_LESSTHANOREQUAL,
    OP_GREATERTHANOREQUAL,
    OP_EQUAL,
    OP_NOTEQUAL,
    OP_BINARYAND,
    OP_BINARYXOR,
    OP_BINARYOR,
    OP_LOGICALAND,
    OP_LOGICALOR,
    OP_ASSIGN,
    OP_MULASSIGN,
    OP_DIVASSIGN,
    OP_MODASSIGN,
    OP_ADDASSIGN,
    OP_SUBASSIGN,
    OP_LSHIFTASSIGN,
    OP_RSHIFTASSIGN,
    OP_ANDASSIGN,
    OP_XORASSIGN,
    OP_ORASSIGN,
    OP_END_RANGE_BINARY,

    OP_START_RANGE_TERNARY,
    OP_CONDITIONAL,
    OP_END_RANGE_TERNARY,

    OP_START_RANGE_DATA,
    OP_IDENTIFIER,
    OP_INT_LITERAL,
    OP_FLOAT_LITERAL,
    OP_STRING_LITERAL,
    OP_END_RANGE_DATA,
} Operator;

static inline int operator_is_unary(const Operator op)
{
    return ((op > OP_START_RANGE_UNARY) && (op < OP_END_RANGE_UNARY));
} // operator_is_unary

static inline int operator_is_binary(const Operator op)
{
    return ((op > OP_START_RANGE_BINARY) && (op < OP_END_RANGE_BINARY));
} // operator_is_binary

static inline int operator_is_ternary(const Operator op)
{
    return ((op > OP_START_RANGE_TERNARY) && (op < OP_END_RANGE_TERNARY));
} // operator_is_ternary


typedef struct Expression
{
    Operator op;  // operator
} Expression;

#define NEW_EXPR(cls) \
    cls *retval = Malloc(ctx, sizeof (cls)); \
    if (retval == NULL) { return NULL; }

typedef struct ExpressionUnary
{
    Operator op;  // operator
    Expression *operand;
} ExpressionUnary;

typedef struct ExpressionBinary
{
    Operator op;  // operator
    Expression *left;
    Expression *right;
} ExpressionBinary;

typedef struct ExpressionTernary
{
    Operator op;  // operator
    Expression *left;
    Expression *center;
    Expression *right;
} ExpressionTernary;

typedef struct ExpressionIdentifier
{
    Operator op;  // Always OP_IDENTIFIER
    const char *identifier;
} ExpressionIdentifier;

typedef struct ExpressionIntLiteral
{
    Operator op;  // Always OP_INT_LITERAL
    int64 value;
} ExpressionIntLiteral;

typedef struct ExpressionFloatLiteral
{
    Operator op;  // Always OP_FLOAT_LITERAL
    double value;
} ExpressionFloatLiteral;

typedef struct ExpressionStringLiteral
{
    Operator op;  // Always OP_STRING_LITERAL
    const char *string;
} ExpressionStringLiteral;

static Expression *new_unary_expr(Context *ctx, const Operator op,
                                  Expression *operand)
{
    NEW_EXPR(ExpressionUnary);
    assert(operator_is_unary(op));
    retval->op = op;
    retval->operand = operand;
    return (Expression *) retval;
} // new_unary_expr

static Expression *new_binary_expr(Context *ctx, const Operator op,
                                   Expression *left, Expression *right)
{
    NEW_EXPR(ExpressionBinary);
    assert(operator_is_binary(op));
    retval->op = op;
    retval->left = left;
    retval->right = right;
    return (Expression *) retval;
} // new_binary_expr

static Expression *new_ternary_expr(Context *ctx, const Operator op,
                                    Expression *left, Expression *center,
                                    Expression *right)
{
    NEW_EXPR(ExpressionTernary);
    assert(operator_is_ternary(op));
    retval->op = op;
    retval->left = left;
    retval->center = center;
    retval->right = right;
    return (Expression *) retval;
} // new_ternary_expr

static Expression *new_identifier_expr(Context *ctx, const char *string)
{
    NEW_EXPR(ExpressionIdentifier);
    retval->op = OP_IDENTIFIER;
    retval->identifier = string;  // cached; don't copy string!
    return (Expression *) retval;
} // new_identifier_expr

static inline int64 strtoi64(const char *str, unsigned int len)
{
    int64 retval = 0;
    int64 mult = 1;
    int i = 0;

    while ((len) && (*str == ' '))
    {
        str++;
        len--;
    } // while

    if ((len) && (*str == '-'))
    {
        mult = -1;
        str++;
        len--;
    } // if

    while (i < len)
    {
        const char ch = str[i];
        if ((ch < '0') || (ch > '9'))
            break;
        i++;
    } // while

    while (--i >= 0)
    {
        const char ch = str[i];
        retval += ((int64) (ch - '0')) * mult;
        mult *= 10;
    } // while

    return retval;
} // strtoi64

static Expression *new_literal_int_expr(Context *ctx, const int64 value)
{
    NEW_EXPR(ExpressionIntLiteral);
    retval->op = OP_INT_LITERAL;
    retval->value = value;
    return (Expression *) retval;
} // new_literal_int_expr

static inline double strtodouble(const char *_str, unsigned int len)
{
    // !!! FIXME: laziness prevails.
    char *str = (char *) alloca(len+1);
    memcpy(str, _str, len);
    str[len] = '\0';
    return strtod(str, NULL);
} // strtodouble

static Expression *new_literal_float_expr(Context *ctx, const double value)
{
    NEW_EXPR(ExpressionFloatLiteral);
    retval->op = OP_FLOAT_LITERAL;
    retval->value = value;
    return (Expression *) retval;
} // new_literal_float_expr

static Expression *new_literal_string_expr(Context *ctx, const char *string)
{
    NEW_EXPR(ExpressionStringLiteral);
    retval->op = OP_STRING_LITERAL;
    retval->string = string;  // cached; don't copy string!
    return (Expression *) retval;
} // new_string_literal_expr

static void print_expr(const Expression *expr, const int depth)
{
    int i;
    for (i = 0; i < depth; i++)
        printf("    ");

    printf("Expression ");
    switch (expr->op)
    {
        #define PRINT_OP(op) case op: printf("%s\n", #op); break;
        PRINT_OP(OP_DEREF_ARRAY);
        PRINT_OP(OP_CALLFUNC);
        PRINT_OP(OP_DEREF_STRUCT);
        PRINT_OP(OP_POSTINCREMENT);
        PRINT_OP(OP_POSTDECREMENT);
        PRINT_OP(OP_COMMA);
        PRINT_OP(OP_PREINCREMENT);
        PRINT_OP(OP_PREDECREMENT);
        PRINT_OP(OP_NEGATE);
        PRINT_OP(OP_COMPLEMENT);
        PRINT_OP(OP_NOT);
        PRINT_OP(OP_MULTIPLY);
        PRINT_OP(OP_DIVIDE);
        PRINT_OP(OP_MODULO);
        PRINT_OP(OP_ADD);
        PRINT_OP(OP_SUBTRACT);
        PRINT_OP(OP_LSHIFT);
        PRINT_OP(OP_RSHIFT);
        PRINT_OP(OP_LESSTHAN);
        PRINT_OP(OP_GREATERTHAN);
        PRINT_OP(OP_LESSTHANOREQUAL);
        PRINT_OP(OP_GREATERTHANOREQUAL);
        PRINT_OP(OP_EQUAL);
        PRINT_OP(OP_NOTEQUAL);
        PRINT_OP(OP_BINARYAND);
        PRINT_OP(OP_BINARYXOR);
        PRINT_OP(OP_BINARYOR);
        PRINT_OP(OP_LOGICALAND);
        PRINT_OP(OP_LOGICALOR);
        PRINT_OP(OP_CONDITIONAL);
        PRINT_OP(OP_ASSIGN);
        PRINT_OP(OP_MULASSIGN);
        PRINT_OP(OP_DIVASSIGN);
        PRINT_OP(OP_MODASSIGN);
        PRINT_OP(OP_ADDASSIGN);
        PRINT_OP(OP_SUBASSIGN);
        PRINT_OP(OP_LSHIFTASSIGN);
        PRINT_OP(OP_RSHIFTASSIGN);
        PRINT_OP(OP_ANDASSIGN);
        PRINT_OP(OP_XORASSIGN);
        PRINT_OP(OP_ORASSIGN);
        PRINT_OP(OP_INT_LITERAL);
        PRINT_OP(OP_FLOAT_LITERAL);
        PRINT_OP(OP_STRING_LITERAL);
        PRINT_OP(OP_IDENTIFIER);
        default: printf("---UNKNOWN!---\n"); return;
    } // switch

    if (operator_is_unary(expr->op))
    {
        const ExpressionUnary *unary = (const ExpressionUnary *) expr;
        print_expr(unary->operand, depth + 1);
    } // if
    else if (operator_is_binary(expr->op))
    {
        const ExpressionBinary *binary = (const ExpressionBinary *) expr;
        print_expr(binary->left, depth + 1);
        print_expr(binary->right, depth + 1);
    } // else if
    else if (operator_is_ternary(expr->op))
    {
        const ExpressionTernary *ternary = (const ExpressionTernary *) expr;
        print_expr(ternary->left, depth + 1);
        print_expr(ternary->center, depth + 1);
        print_expr(ternary->right, depth + 1);
    } // else if

    else
    {
        for (i = 0; i < (depth + 1); i++)
            printf("    ");

        if (expr->op == OP_IDENTIFIER)
        {
            const ExpressionIdentifier *ident = (const ExpressionIdentifier *) expr;
            printf("(%s)\n", ident->identifier);
        } // if
        else if (expr->op == OP_INT_LITERAL)
        {
            const ExpressionIntLiteral *lit = (const ExpressionIntLiteral *) expr;
            printf("(%lld)\n", (long long) lit->value);
        } // if
        else if (expr->op == OP_FLOAT_LITERAL)
        {
            const ExpressionFloatLiteral *lit = (const ExpressionFloatLiteral *) expr;
            printf("(%lf)\n", lit->value);
        } // if
        else if (expr->op == OP_STRING_LITERAL)
        {
            const ExpressionStringLiteral *lit = (const ExpressionStringLiteral *) expr;
            printf("(\"%s\")\n", lit->string);
        } // if
        else
        {
            assert(0 && "Shouldn't hit this.");
        } // else
    } // else
} // print_expr

static double run_expr(const Expression *expr)
{
    if (operator_is_unary(expr->op))
    {
        const ExpressionUnary *unary = (const ExpressionUnary *) expr;
        if (expr->op == OP_NEGATE)
            return -run_expr(unary->operand);
        else if (expr->op == OP_COMPLEMENT)
            return (double) (~((int64)run_expr(unary->operand)));
        else if (expr->op == OP_NOT)
            return (run_expr(unary->operand) == 0.0) ? 1.0 : 0.0;
    } // if
    else if (operator_is_binary(expr->op))
    {
        const ExpressionBinary *binary = (const ExpressionBinary *) expr;
        if (expr->op == OP_MULTIPLY)
            return run_expr(binary->left) * run_expr(binary->right);
        else if (expr->op == OP_DIVIDE)
            return run_expr(binary->left) / run_expr(binary->right);
        else if (expr->op == OP_ADD)
            return run_expr(binary->left) + run_expr(binary->right);
        else if (expr->op == OP_SUBTRACT)
            return run_expr(binary->left) - run_expr(binary->right);
        else if (expr->op == OP_LESSTHAN)
            return (run_expr(binary->left) < run_expr(binary->right)) ? 1.0 : 0.0;
        else if (expr->op == OP_GREATERTHAN)
            return (run_expr(binary->left) > run_expr(binary->right)) ? 1.0 : 0.0;
        else if (expr->op == OP_LESSTHANOREQUAL)
            return (run_expr(binary->left) <= run_expr(binary->right)) ? 1.0 : 0.0;
        else if (expr->op == OP_GREATERTHANOREQUAL)
            return (run_expr(binary->left) >= run_expr(binary->right)) ? 1.0 : 0.0;
        else if (expr->op == OP_EQUAL)
            return (run_expr(binary->left) == run_expr(binary->right)) ? 1.0 : 0.0;
        else if (expr->op == OP_NOTEQUAL)
            return (run_expr(binary->left) == run_expr(binary->right)) ? 1.0 : 0.0;
        else if (expr->op == OP_LOGICALAND)
            return (((int64)run_expr(binary->left)) && ((int64)run_expr(binary->right))) ? 1.0 : 0.0;
        else if (expr->op == OP_LOGICALOR)
            return (((int64)run_expr(binary->left)) || ((int64)run_expr(binary->right))) ? 1.0 : 0.0;
        else if (expr->op == OP_BINARYAND)
            return (double)(((int64)run_expr(binary->left)) & ((int64)run_expr(binary->right)));
        else if (expr->op == OP_BINARYOR)
            return (double)(((int64)run_expr(binary->left)) | ((int64)run_expr(binary->right)));
        else if (expr->op == OP_BINARYXOR)
            return (double)(((int64)run_expr(binary->left)) ^ ((int64)run_expr(binary->right)));
        else if (expr->op == OP_LSHIFT)
            return (double)(((int64)run_expr(binary->left)) << ((int64)run_expr(binary->right)));
        else if (expr->op == OP_RSHIFT)
            return (double)(((int64)run_expr(binary->left)) >> ((int64)run_expr(binary->right)));
        else if (expr->op == OP_MODULO)
            return (double)(((int64)run_expr(binary->left)) % ((int64)run_expr(binary->right)));
    } // else if

    else if (operator_is_ternary(expr->op))
    {
        const ExpressionTernary *ternary = (const ExpressionTernary *) expr;
        if (expr->op == OP_CONDITIONAL)
            return (run_expr(ternary->left) != 0.0) ? run_expr(ternary->center) : run_expr(ternary->right);
    } // else if

    else
    {
        if (expr->op == OP_INT_LITERAL)
        {
            const ExpressionIntLiteral *lit = (const ExpressionIntLiteral *) expr;
            return ((double) lit->value);
        } // if
        else if (expr->op == OP_FLOAT_LITERAL)
        {
            const ExpressionFloatLiteral *lit = (const ExpressionFloatLiteral *) expr;
            return lit->value;
        } // if
    } // else

    return 0.0;  // oh well.
} // run_expr

static void free_expr(Context *ctx, Expression *expr)
{
    if (operator_is_unary(expr->op))
    {
        const ExpressionUnary *unary = (const ExpressionUnary *) expr;
        free_expr(ctx, unary->operand);
    } // if
    else if (operator_is_binary(expr->op))
    {
        const ExpressionBinary *binary = (const ExpressionBinary *) expr;
        free_expr(ctx, binary->left);
        free_expr(ctx, binary->right);
    } // else if
    else if (operator_is_ternary(expr->op))
    {
        const ExpressionTernary *ternary = (const ExpressionTernary *) expr;
        free_expr(ctx, ternary->left);
        free_expr(ctx, ternary->center);
        free_expr(ctx, ternary->right);
    } // else if

    // don't need to free extra fields in other types at the moment.

    Free(ctx, expr);
} // free_expr

static void parse_complete(Context *ctx, Expression *expr)
{
    print_expr(expr, 0);
    printf("Result: %lf\n\n", run_expr(expr));
    free_expr(ctx, expr);
} // parse_complete


// !!! FIXME: sort of cut-and-paste from the preprocessor...

// this is djb's xor hashing function.
static inline uint32 hash_string_djbxor(const char *str, unsigned int len)
{
    register uint32 hash = 5381;
    while (len--)
        hash = ((hash << 5) + hash) ^ *(str++);
    return hash;
} // hash_string_djbxor

static inline uint8 hash_string(const char *str, const unsigned int len)
{
    return (uint8) hash_string_djbxor(str, len);
} // hash_string

static const char *cache_string(Context *ctx, const char *str,
                                const unsigned int len)
{
    const uint8 hash = hash_string(str, len);
    StringBucket *bucket = ctx->string_hashtable[hash];
    StringBucket *prev = NULL;
    while (bucket)
    {
        const char *bstr = bucket->string;
        if ((strncmp(bstr, str, len) == 0) && (bstr[len] == 0))
        {
            // Matched! Move this to the front of the list.
            if (prev != NULL)
            {
                assert(prev->next == bucket);
                prev->next = bucket->next;
                bucket->next = ctx->string_hashtable[hash];
                ctx->string_hashtable[hash] = bucket;
            } // if
            return bstr; // already cached
        } // if
        prev = bucket;
        bucket = bucket->next;
    } // while

    // no match, add to the table.
    bucket = (StringBucket *) Malloc(ctx, sizeof (StringBucket));
    if (bucket == NULL)
        return NULL;
    bucket->string = (char *) Malloc(ctx, len + 1);
    if (bucket->string == NULL)
    {
        Free(ctx, bucket);
        return NULL;
    } // if
    memcpy(bucket->string, str, len);
    bucket->string[len] = '\0';
    bucket->next = ctx->string_hashtable[hash];
    ctx->string_hashtable[hash] = bucket;
    return bucket->string;
} // cache_string

static void free_string_cache(Context *ctx)
{
    size_t i;
    for (i = 0; i < STATICARRAYLEN(ctx->string_hashtable); i++)
    {
        StringBucket *bucket = ctx->string_hashtable[i];
        ctx->string_hashtable[i] = NULL;
        while (bucket)
        {
            StringBucket *next = bucket->next;
            Free(ctx, bucket->string);
            Free(ctx, bucket);
            bucket = next;
        } // while
    } // for
} // free_string_cache


// This is where the actual parsing happens. It's Lemon-generated!
#define __MOJOSHADER_CALC_COMPILER__ 1
#include "calculator.h"

static int convert_to_lemon_token(const Context *ctx, const Token tokenval)
{
    switch (tokenval)
    {
        case ((Token) ','): return TOKEN_CALC_COMMA;
        case ((Token) '='): return TOKEN_CALC_ASSIGN;
        case ((Token) TOKEN_ADDASSIGN): return TOKEN_CALC_ADDASSIGN;
        case ((Token) TOKEN_SUBASSIGN): return TOKEN_CALC_SUBASSIGN;
        case ((Token) TOKEN_MULTASSIGN): return TOKEN_CALC_MULASSIGN;
        case ((Token) TOKEN_DIVASSIGN): return TOKEN_CALC_DIVASSIGN;
        case ((Token) TOKEN_MODASSIGN): return TOKEN_CALC_MODASSIGN;
        case ((Token) TOKEN_LSHIFTASSIGN): return TOKEN_CALC_LSHIFTASSIGN;
        case ((Token) TOKEN_RSHIFTASSIGN): return TOKEN_CALC_RSHIFTASSIGN;
        case ((Token) TOKEN_ANDASSIGN): return TOKEN_CALC_ANDASSIGN;
        case ((Token) TOKEN_ORASSIGN): return TOKEN_CALC_ORASSIGN;
        case ((Token) TOKEN_XORASSIGN): return TOKEN_CALC_XORASSIGN;
        case ((Token) '?'): return TOKEN_CALC_QUESTION;
        case ((Token) TOKEN_OROR): return TOKEN_CALC_OROR;
        case ((Token) TOKEN_ANDAND): return TOKEN_CALC_ANDAND;
        case ((Token) '|'): return TOKEN_CALC_OR;
        case ((Token) '^'): return TOKEN_CALC_XOR;
        case ((Token) '&'): return TOKEN_CALC_AND;
        case ((Token) TOKEN_EQL): return TOKEN_CALC_EQL;
        case ((Token) TOKEN_NEQ): return TOKEN_CALC_NEQ;
        case ((Token) '<'): return TOKEN_CALC_LT;
        case ((Token) TOKEN_LEQ): return TOKEN_CALC_LEQ;
        case ((Token) '>'): return TOKEN_CALC_GT;
        case ((Token) TOKEN_GEQ): return TOKEN_CALC_GEQ;
        case ((Token) TOKEN_LSHIFT): return TOKEN_CALC_LSHIFT;
        case ((Token) TOKEN_RSHIFT): return TOKEN_CALC_RSHIFT;
        case ((Token) '+'): return TOKEN_CALC_PLUS;
        case ((Token) '-'): return TOKEN_CALC_MINUS;
        case ((Token) '*'): return TOKEN_CALC_STAR;
        case ((Token) '/'): return TOKEN_CALC_SLASH;
        case ((Token) '%'): return TOKEN_CALC_PERCENT;
        case ((Token) '!'): return TOKEN_CALC_EXCLAMATION;
        case ((Token) '~'): return TOKEN_CALC_COMPLEMENT;
        case ((Token) TOKEN_DECREMENT): return TOKEN_CALC_MINUSMINUS;
        case ((Token) TOKEN_INCREMENT): return TOKEN_CALC_PLUSPLUS;
        case ((Token) '.'): return TOKEN_CALC_DOT;
        case ((Token) '['): return TOKEN_CALC_LBRACKET;
        case ((Token) ']'): return TOKEN_CALC_RBRACKET;
        case ((Token) '('): return TOKEN_CALC_LPAREN;
        case ((Token) ')'): return TOKEN_CALC_RPAREN;
        case ((Token) TOKEN_INT_LITERAL): return TOKEN_CALC_INT_CONSTANT;
        case ((Token) TOKEN_FLOAT_LITERAL): return TOKEN_CALC_FLOAT_CONSTANT;
        case ((Token) TOKEN_STRING_LITERAL): return TOKEN_CALC_STRING_LITERAL;
        case ((Token) ':'): return TOKEN_CALC_COLON;
        //case ((Token) ';'): return TOKEN_CALC_SEMICOLON;
        //case ((Token) '{'): return TOKEN_CALC_LBRACE;
        //case ((Token) '}'): return TOKEN_CALC_RBRACE;
        case ((Token) TOKEN_IDENTIFIER): return TOKEN_CALC_IDENTIFIER;
        case TOKEN_EOI: return 0;
        case TOKEN_BAD_CHARS: printf("bad chars from lexer\n"); return 0;
        case TOKEN_PREPROCESSING_ERROR: printf("error from lexer\n"); return 0;
        default: assert(0 && "unexpected token from lexer\n"); return 0;
    } // switch

    return 0;
} // convert_to_lemon_token

static void MOJOSHADER_compile(const char *filename,
                             const char *source, unsigned int sourcelen,
                             const MOJOSHADER_preprocessorDefine *defines,
                             unsigned int define_count,
                             MOJOSHADER_includeOpen include_open,
                             MOJOSHADER_includeClose include_close,
                             MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
{
    Context ctx;
    TokenData data;
    unsigned int tokenlen;
    Token tokenval;
    const char *token;
    int lemon_token;

    if (m == NULL) m = MOJOSHADER_internal_malloc;
    if (f == NULL) f = MOJOSHADER_internal_free;

    memset(&ctx, '\0', sizeof (Context));
    ctx.malloc = m;
    ctx.free = f;
    ctx.malloc_data = d;
    ctx.preprocessor = preprocessor_start(filename, source, sourcelen,
                                           include_open, include_close,
                                           defines, define_count, 0, m, f, d);

    void *pParser = ParseCalculatorAlloc(m, d);

    #if DEBUG_COMPILER_PARSER
    ParseCalculatorTrace(stdout, "COMPILER: ");
    #endif

    do {
        token = preprocessor_nexttoken(ctx.preprocessor, &tokenlen, &tokenval);
        lemon_token = convert_to_lemon_token(&ctx, tokenval);
        switch (lemon_token)
        {
            case TOKEN_CALC_INT_CONSTANT:
                data.i64 = strtoi64(token, tokenlen);
                break;

            case TOKEN_CALC_FLOAT_CONSTANT:
                data.dbl = strtodouble(token, tokenlen);
                break;

            case TOKEN_CALC_STRING_LITERAL:
            case TOKEN_CALC_IDENTIFIER:
                data.string = cache_string(&ctx, token, tokenlen);
                break;

            default:
                data.i64 = 0;
                break;
        } // switch

        ParseCalculator(pParser, lemon_token, data, &ctx);
    } while ((!ctx.isfail) && (tokenval != TOKEN_EOI));

    ParseCalculatorFree(pParser, f, d);
    // !!! FIXME: destruct (ctx) here.
    free_string_cache(&ctx);
} // MOJOSHADER_compile

int main(int argc, char **argv)
{
    const char *ln;
    size_t len = 0;
    FILE *io = stdin;
    const char *filename = "<stdin>";

    while ((ln = fgetln(io, &len)) != NULL)
    {
        if (len == 1)
            continue;
        else if ((len == 5) && (memcmp(ln, "quit\n", 5) == 0))
            break;
        else if ((len == 2) && (memcmp(ln, "q\n", 2) == 0))
            break;

        MOJOSHADER_compile(filename, ln, (unsigned int) len,
                           NULL, 0, NULL, NULL, NULL, NULL, NULL);
    } // while

    fclose(io);
    return 0;
} // main

// end of calculator.c ...