"use strict";
// -*- mode: typescript; indent-tabs-mode: nil; js-basic-offset: 4 -*-
//
// This file is part of Genie
//
// Copyright 2019-2020 The Board of Trustees of the Leland Stanford Junior University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Giovanni Campagna <gcampagn@cs.stanford.edu>
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Choice = exports.NonTerminal = exports.Context = exports.Derivation = exports.LogLevel = exports.import = void 0;
const assert_1 = __importDefault(require("assert"));
const list_1 = __importDefault(require("./list"));
const random_1 = require("../utils/random");
var compiler_1 = require("./compiler");
Object.defineProperty(exports, "import", { enumerable: true, get: function () { return compiler_1.importGenie; } });
const LogLevel = {
    NONE: 0,
    // log at the beginning and at the end of the generation for each depth, and notable events
    // such as particularly slow templates
    INFO: 1,
    // log each non-empty non terminal
    GENERATION: 2,
    // log each non-empty non terminal, and additional verbose information
    VERBOSE_GENERATION: 3,
    // log all templates before generation
    DUMP_TEMPLATES: 4,
    // log information derived from the templates (such as the distance from the root)
    DUMP_DERIVED: 5,
    // log a lot of very redundant information during generation (can cause slowdowns)
    EVERYTHING: 6
};
exports.LogLevel = LogLevel;
/**
 * A reference to a context.
 *
 * A context is an object that is passed as extra input to a semantic function
 * to affect its behavior. Grammar rules are only applied between identical (===) contexts.
 *
 * The "context" in this definition roughly corresponds to a dialogue context
 * (either a C: state, or a more general notion) but it need not be.
 *
 * "value" is a value associated with the context that is only meaningful to the API caller
 * (DialogueGenerator).
 */
class Context {
    constructor(value) {
        this.value = value;
        // NOTE: this assumes that no more than ~4B contexts exists, otherwise
        // this will overflow
        this._id = Context._nextId++;
    }
    toString() {
        return `CTX[${this.value}]`;
    }
    hash() {
        return this._id;
    }
    equals(other) {
        return this === other;
    }
    static compatible(c1, c2) {
        return c1 === null || c2 === null || c1 === c2;
    }
    static meet(c1, c2) {
        if (c1 === null)
            return c2;
        else
            return c1;
    }
}
exports.Context = Context;
Context._nextId = 0;
// A Derivation represents a sentence, possibly with placeholders,
// and a value, possibly with unspecified input parameters, that
// was computed at a certain point in the derivation tree
class Derivation {
    constructor(key, value, sentence, context = null, priority = 0) {
        this.key = key;
        this.value = value;
        if (value === undefined)
            throw new TypeError('Invalid value');
        this.context = context;
        assert_1.default(typeof this.context === 'object'); // incl. null
        this.sentence = sentence;
        this.priority = priority;
        assert_1.default(Number.isFinite(this.priority));
        assert_1.default(sentence instanceof list_1.default);
        this._flatSentence = null;
    }
    toString() {
        if (this._flatSentence)
            return this._flatSentence;
        const flattened = [];
        this.sentence.traverse((el) => flattened.push(String(el)));
        return this._flatSentence = flattened.join(' ');
    }
    clone() {
        return new Derivation(this.key, this.value, this.sentence, this.context, this.priority);
    }
    static combine(children, semanticAction, keyFunction, rulePriority) {
        if (children.length === 1) {
            assert_1.default(!(children[0] instanceof Context));
            if (children[0] instanceof Derivation) {
                const newValue = semanticAction(...[children[0].value]);
                assert_1.default(newValue !== undefined);
                if (newValue === null)
                    return null;
                const newKey = keyFunction(newValue);
                const newDerivation = new Derivation(newKey, newValue, children[0].sentence, children[0].context, children[0].priority);
                newDerivation.priority += rulePriority;
                return newDerivation;
            }
            else { // constant or terminal
                const newValue = semanticAction(...[undefined]);
                assert_1.default(newValue !== undefined);
                if (newValue === null)
                    return null;
                const newKey = keyFunction(newValue);
                return new Derivation(newKey, newValue, list_1.default.singleton(children[0]), null, rulePriority);
            }
        }
        let newSentence = list_1.default.Nil;
        const values = [];
        let newContext = null;
        let newPriority = rulePriority;
        for (const child of children) {
            if (typeof child === 'string') { // terminal
                values.push(undefined);
                newSentence = list_1.default.append(newSentence, child);
            }
            else {
                assert_1.default(Context.compatible(newContext, child.context));
                newContext = Context.meet(newContext, child.context);
                newPriority += child.priority;
                values.push(child.value);
                newSentence = list_1.default.join(newSentence, child.sentence);
            }
        }
        //console.log('combine: ' + children.join(' ++ '));
        //console.log('values: ' + values.join(' , '));
        const newValue = semanticAction(...values);
        assert_1.default(newValue !== undefined);
        if (newValue === null)
            return null;
        const newKey = keyFunction(newValue);
        return new Derivation(newKey, newValue, newSentence, newContext, newPriority);
    }
}
exports.Derivation = Derivation;
class NonTerminal {
    constructor(symbol, constraint = undefined) {
        this.relativeKeyConstraint = undefined;
        this.constantKeyConstraint = undefined;
        this.symbol = symbol;
        this.index = -1;
        if (constraint) {
            if (constraint.length === 3)
                this.relativeKeyConstraint = constraint;
            else
                this.constantKeyConstraint = constraint;
        }
    }
    toString() {
        return `NT[${this.symbol}]`;
    }
}
exports.NonTerminal = NonTerminal;
class Choice {
    constructor(choices) {
        this.choices = choices;
    }
    choose(rng) {
        return random_1.uniform(this.choices, rng);
    }
    toString() {
        return `C[${this.choices.join('|')}]`;
    }
}
exports.Choice = Choice;
//# sourceMappingURL=runtime.js.map