"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Rules_1 = require("./Rules");
const graphql_1 = require("graphql");
function onlineParser(options = {
    eatWhitespace: stream => stream.eatWhile(Rules_1.isIgnored),
    lexRules: Rules_1.LexRules,
    parseRules: Rules_1.ParseRules,
    editorConfig: {},
}) {
    return {
        startState() {
            const initialState = {
                level: 0,
                step: 0,
                name: null,
                kind: null,
                type: null,
                rule: null,
                needsSeparator: false,
                prevState: null,
            };
            pushRule(options.parseRules, initialState, graphql_1.Kind.DOCUMENT);
            return initialState;
        },
        token(stream, state) {
            return getToken(stream, state, options);
        },
    };
}
exports.default = onlineParser;
function getToken(stream, state, options) {
    var _a;
    if (state.inBlockstring) {
        if (stream.match(/.*"""/)) {
            state.inBlockstring = false;
            return 'string';
        }
        stream.skipToEnd();
        return 'string';
    }
    const { lexRules, parseRules, eatWhitespace, editorConfig } = options;
    if (state.rule && state.rule.length === 0) {
        popRule(state);
    }
    else if (state.needsAdvance) {
        state.needsAdvance = false;
        advanceRule(state, true);
    }
    if (stream.sol()) {
        const tabSize = (editorConfig === null || editorConfig === void 0 ? void 0 : editorConfig.tabSize) || 2;
        state.indentLevel = Math.floor(stream.indentation() / tabSize);
    }
    if (eatWhitespace(stream)) {
        return 'ws';
    }
    const token = lex(lexRules, stream);
    if (!token) {
        const matchedSomething = stream.match(/\S+/);
        if (!matchedSomething) {
            stream.match(/\s/);
        }
        pushRule(SpecialParseRules, state, 'Invalid');
        return 'invalidchar';
    }
    if (token.kind === 'Comment') {
        pushRule(SpecialParseRules, state, 'Comment');
        return 'comment';
    }
    const backupState = assign({}, state);
    if (token.kind === 'Punctuation') {
        if (/^[{([]/.test(token.value)) {
            if (state.indentLevel !== undefined) {
                state.levels = (state.levels || []).concat(state.indentLevel + 1);
            }
        }
        else if (/^[})\]]/.test(token.value)) {
            const levels = (state.levels = (state.levels || []).slice(0, -1));
            if (state.indentLevel &&
                levels.length > 0 &&
                levels.at(-1) < state.indentLevel) {
                state.indentLevel = levels.at(-1);
            }
        }
    }
    while (state.rule) {
        let expected = typeof state.rule === 'function'
            ? state.step === 0
                ? state.rule(token, stream)
                : null
            : state.rule[state.step];
        if (state.needsSeparator) {
            expected = expected === null || expected === void 0 ? void 0 : expected.separator;
        }
        if (expected) {
            if (expected.ofRule) {
                expected = expected.ofRule;
            }
            if (typeof expected === 'string') {
                pushRule(parseRules, state, expected);
                continue;
            }
            if ((_a = expected.match) === null || _a === void 0 ? void 0 : _a.call(expected, token)) {
                if (expected.update) {
                    expected.update(state, token);
                }
                if (token.kind === 'Punctuation') {
                    advanceRule(state, true);
                }
                else {
                    state.needsAdvance = true;
                }
                return expected.style;
            }
        }
        unsuccessful(state);
    }
    assign(state, backupState);
    pushRule(SpecialParseRules, state, 'Invalid');
    return 'invalidchar';
}
function assign(to, from) {
    const keys = Object.keys(from);
    for (let i = 0; i < keys.length; i++) {
        to[keys[i]] = from[keys[i]];
    }
    return to;
}
const SpecialParseRules = {
    Invalid: [],
    Comment: [],
};
function pushRule(rules, state, ruleKind) {
    if (!rules[ruleKind]) {
        throw new TypeError('Unknown rule: ' + ruleKind);
    }
    state.prevState = Object.assign({}, state);
    state.kind = ruleKind;
    state.name = null;
    state.type = null;
    state.rule = rules[ruleKind];
    state.step = 0;
    state.needsSeparator = false;
}
function popRule(state) {
    if (!state.prevState) {
        return;
    }
    state.kind = state.prevState.kind;
    state.name = state.prevState.name;
    state.type = state.prevState.type;
    state.rule = state.prevState.rule;
    state.step = state.prevState.step;
    state.needsSeparator = state.prevState.needsSeparator;
    state.prevState = state.prevState.prevState;
}
function advanceRule(state, successful) {
    var _a;
    if (isList(state) && state.rule) {
        const step = state.rule[state.step];
        if (step.separator) {
            const { separator } = step;
            state.needsSeparator = !state.needsSeparator;
            if (!state.needsSeparator && separator.ofRule) {
                return;
            }
        }
        if (successful) {
            return;
        }
    }
    state.needsSeparator = false;
    state.step++;
    while (state.rule &&
        !(Array.isArray(state.rule) && state.step < state.rule.length)) {
        popRule(state);
        if (state.rule) {
            if (isList(state)) {
                if ((_a = state.rule) === null || _a === void 0 ? void 0 : _a[state.step].separator) {
                    state.needsSeparator = !state.needsSeparator;
                }
            }
            else {
                state.needsSeparator = false;
                state.step++;
            }
        }
    }
}
function isList(state) {
    const step = Array.isArray(state.rule) &&
        typeof state.rule[state.step] !== 'string' &&
        state.rule[state.step];
    return step && step.isList;
}
function unsuccessful(state) {
    while (state.rule &&
        !(Array.isArray(state.rule) && state.rule[state.step].ofRule)) {
        popRule(state);
    }
    if (state.rule) {
        advanceRule(state, false);
    }
}
function lex(lexRules, stream) {
    const kinds = Object.keys(lexRules);
    for (let i = 0; i < kinds.length; i++) {
        const match = stream.match(lexRules[kinds[i]]);
        if (match && match instanceof Array) {
            return { kind: kinds[i], value: match[0] };
        }
    }
}
//# sourceMappingURL=onlineParser.js.map