"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parse = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const good_enough_parser_1 = require("good-enough-parser");
const hasha_1 = tslib_1.__importDefault(require("hasha"));
const memCache = tslib_1.__importStar(require("../../../util/cache/memory"));
const common_1 = require("./common");
function isTarget(target) {
    return !!target.rule && !!target.name;
}
const emptyCtx = {
    result: { targets: [], meta: [] },
    currentTarget: {},
    currentMetaPath: [],
    ruleIndex: 0,
};
const starlark = good_enough_parser_1.lang.createLang('starlark');
/**
 * Matches rule type:
 * - `git_repository`
 * - `go_repository`
 **/
const ruleSym = good_enough_parser_1.query.sym(common_1.supportedRulesRegex, (ctx, { value, offset }) => {
    ctx.currentTarget.rule = value;
    // TODO: remove it (#9667)
    if (!is_1.default.number(ctx.ruleStartOffset)) {
        ctx.ruleStartOffset = offset;
    }
    return ctx;
});
/**
 * Matches key-value pairs:
 * - `tag = "1.2.3"`
 * - `name = "foobar"`
 * - `deps = ["foo", "bar"]`
 **/
const kwParams = good_enough_parser_1.query
    .sym((ctx, { value }) => {
    ctx.currentAttrKey = value;
    const leaf = ctx.currentMetaPath.pop();
    if (is_1.default.number(leaf)) {
        ctx.currentMetaPath.push(leaf);
    }
    ctx.currentMetaPath.push(value);
    return ctx;
})
    .op('=')
    .alt(
// string case
good_enough_parser_1.query.str((ctx, { offset, value }) => {
    ctx.currentTarget[ctx.currentAttrKey] = value;
    ctx.result.meta.push({
        path: [...ctx.currentMetaPath],
        data: { offset: offset, length: value.length },
    });
    return ctx;
}), 
// array of strings case
good_enough_parser_1.query.tree({
    type: 'wrapped-tree',
    maxDepth: 1,
    preHandler: (ctx) => {
        ctx.currentArray = [];
        return ctx;
    },
    search: good_enough_parser_1.query.str((ctx, { value, offset }) => {
        ctx.result.meta.push({
            path: [...ctx.currentMetaPath, ctx.currentArray.length],
            data: { offset: offset, length: value.length },
        });
        ctx.currentArray?.push(value);
        return ctx;
    }),
    postHandler: (ctx) => {
        ctx.currentTarget[ctx.currentAttrKey] = ctx.currentArray;
        ctx.currentArray = [];
        return ctx;
    },
}));
/**
 * Matches rule signature, i.e. content of `git_repository(...)`
 *
 * @param search something to match inside parens
 */
function ruleCall(search) {
    return good_enough_parser_1.query.tree({
        type: 'wrapped-tree',
        maxDepth: 1,
        preHandler: (ctx) => {
            ctx.currentMetaPath.push(ctx.ruleIndex);
            return ctx;
        },
        search,
        postHandler: (ctx, tree) => {
            // TODO: remove it (#9667)
            if (is_1.default.number(ctx.ruleStartOffset) && tree.type === 'wrapped-tree') {
                const { children, endsWith } = tree;
                const lastElem = children[children.length - 1];
                if (lastElem.type === '_end') {
                    const ruleEndOffset = lastElem.offset + endsWith.value.length;
                    const offset = ctx.ruleStartOffset;
                    const length = ruleEndOffset - ctx.ruleStartOffset;
                    ctx.result.meta.push({
                        path: [ctx.ruleIndex],
                        data: { offset, length },
                    });
                    delete ctx.ruleStartOffset;
                }
            }
            if (isTarget(ctx.currentTarget)) {
                ctx.result.targets.push(ctx.currentTarget);
            }
            ctx.currentTarget = {};
            ctx.ruleIndex += 1;
            ctx.currentMetaPath.pop();
            return ctx;
        },
    });
}
/**
 * Matches regular rules:
 * - `git_repository(...)`
 * - `go_repository(...)`
 */
const regularRule = ruleSym.join(ruleCall(kwParams));
const maybeFirstArg = good_enough_parser_1.query.begin().join(ruleSym).op(',');
/**
 * Matches "maybe"-form rules:
 * - `maybe(git_repository, ...)`
 * - `maybe(go_repository, ...)`
 */
const maybeRule = good_enough_parser_1.query
    .sym('maybe', 
// TODO: remove it (#9667)
(ctx, { offset }) => {
    if (!is_1.default.number(ctx.ruleStartOffset)) {
        ctx.ruleStartOffset = offset;
    }
    return ctx;
})
    .join(ruleCall(good_enough_parser_1.query.alt(maybeFirstArg, kwParams)));
const rule = good_enough_parser_1.query.alt(maybeRule, regularRule);
const query = good_enough_parser_1.query.tree({
    type: 'root-tree',
    maxDepth: 16,
    search: rule,
});
function getCacheKey(input) {
    const hash = (0, hasha_1.default)(input);
    return `bazel-parser-${hash}`;
}
function parse(input, ctx = emptyCtx) {
    const cacheKey = getCacheKey(input);
    const cachedResult = memCache.get(cacheKey);
    // istanbul ignore if
    if (cachedResult === null || cachedResult) {
        return cachedResult;
    }
    const parsedResult = starlark.query(input, query, ctx);
    let result = null;
    if (parsedResult) {
        result = parsedResult.result;
    }
    memCache.set(cacheKey, result);
    return result;
}
exports.parse = parse;
//# sourceMappingURL=parser.js.map