Source: dialects/ir/index.js

const fs = require('fs-extra');
const { applyIR } = require('../../ir/ir.js');

/**
 * Apps written as an app description (see ir/ir.js): a file <name>.app.json. Importers
 * from other platforms produce the same description.
 */
/**
 * Where the mistake in text is, as {line, column} from 1, given the error JSON.parse threw.
 * Its message says so for some mistakes ("... (line 2 column 3)") but not for others
 * ("Unexpected token ','"); then the mistake is found as the shortest start of the text that
 * fails before its end.
 */
function jsonErrorPosition(text, err) {
    const m = /line (\d+) column (\d+)/.exec(err.message);
    if (m) {
        return { line: Number(m[1]), column: Number(m[2]) };
    }
    const failsBeforeEnd = (n) => {
        try {
            JSON.parse(text.substring(0, n));
            return false;
        } catch (e) {
            // Failing where the start ends, e.g. "Expected property name or '}' in JSON at position 1" for "{".
            const at = /at position (\d+)/.exec(e.message);
            return !/end of JSON input/.test(e.message) && !(at && Number(at[1]) >= n);
        }
    };
    let lo = 0;
    let hi = text.length;
    while (lo < hi) {
        const mid = (lo + hi) >> 1;
        if (failsBeforeEnd(mid + 1)) hi = mid;
        else lo = mid + 1;
    }
    const before = text.substring(0, lo).split('\n');
    return { line: before.length, column: before[before.length - 1].length + 1 };
}

module.exports = {
    name: 'ir',

    detect(appPath) {
        return appPath.endsWith('.app.json');
    },

    /** Makes app what its file describes. */
    define(app) {
        app.appjs = fs.readFileSync(app.appPath, 'utf8');
        let ir;
        try {
            ir = JSON.parse(app.appjs);
        } catch (err) {
            const pos = jsonErrorPosition(app.appjs, err);
            err.jtreePosition = { file: app.appPath, line: String(pos.line), column: String(pos.column) };
            throw err;
        }
        try {
            applyIR(app, ir);
        } catch (err) {
            err.jtreePosition = err.jtreePosition || { file: app.appPath, line: 'unknown', column: 'unknown' };
            throw err;
        }
    },
};