OverviewFilesCommitsIssuesDocsReleasesPeers

mirall-cli / node_modules / unbash / dist / printer.js

435 lines · javascript ref:
export function print(script) {
    let out = "";
    if (script.shebang)
        out += script.shebang + "\n\n";
    out += stmts(script.commands, 0);
    return out;
}
function isFunc(s) {
    return s.command.type === "Function";
}
function stmts(list, indent) {
    let out = "";
    for (let i = 0; i < list.length; i++) {
        if (i > 0)
            out += isFunc(list[i - 1]) || isFunc(list[i]) ? "\n\n" : "\n";
        out += stmt(list[i], indent);
    }
    return out;
}
function stmt(s, indent) {
    const pad = "  ".repeat(indent);
    let out = pad + printNode(s.command, indent);
    for (const r of s.redirects)
        out += " " + redir(r);
    if (s.background)
        out += " &";
    out += heredocBodies(s);
    return out;
}
function heredocBodies(s) {
    let out = "";
    if (s.command.type === "Command") {
        for (const r of s.command.redirects)
            out += heredocBody(r);
    }
    for (const r of s.redirects)
        out += heredocBody(r);
    return out;
}
function heredocBody(r) {
    if (r.operator !== "<<" && r.operator !== "<<-")
        return "";
    if (r.content == null)
        return "";
    return "\n" + r.content + delimName(r);
}
function delimName(r) {
    if (!r.target)
        return "";
    const text = wd(r.target);
    if ((text[0] === "'" && text[text.length - 1] === "'") || (text[0] === '"' && text[text.length - 1] === '"')) {
        return text.slice(1, -1);
    }
    if (text.includes("\\"))
        return text.replaceAll("\\", "");
    return text;
}
function printNode(n, indent) {
    switch (n.type) {
        case "Command":
            return cmd(n);
        case "Pipeline":
            return pipe(n, indent);
        case "AndOr":
            return andOr(n, indent);
        case "If":
            return ifNode(n, indent, false);
        case "For":
            return forNode(n, indent);
        case "While":
            return whileNode(n, indent);
        case "Case":
            return caseNode(n, indent);
        case "Function":
            return funcNode(n, indent);
        case "Subshell":
            return subshell(n, indent);
        case "BraceGroup":
            return braceGroup(n, indent);
        case "CompoundList":
            return stmts(n.commands, indent);
        case "TestCommand":
            return testCmd(n);
        case "ArithmeticCommand":
            return arithCmd(n);
        case "Select":
            return selectNode(n, indent);
        case "ArithmeticFor":
            return arithFor(n, indent);
        case "Coproc":
            return coprocNode(n, indent);
        case "Statement": {
            let out = printNode(n.command, indent);
            for (const r of n.redirects)
                out += " " + redir(r);
            if (n.background)
                out += " &";
            return out;
        }
    }
}
function wd(w) {
    if (!w.parts)
        return w.text;
    let out = "";
    for (const p of w.parts)
        out += p.text;
    return out;
}
function assign(a) {
    let out = a.name ?? "";
    if (a.index != null)
        out += "[" + a.index + "]";
    out += a.append ? "+=" : "=";
    if (a.array) {
        out += "(" + a.array.map((w) => wd(w)).join(" ") + ")";
    }
    else if (a.value) {
        out += wd(a.value);
    }
    return out;
}
function cmd(c) {
    const parts = [];
    for (const a of c.prefix)
        parts.push(assign(a));
    if (c.name)
        parts.push(wd(c.name));
    for (const s of c.suffix)
        parts.push(wd(s));
    for (const r of c.redirects)
        parts.push(redir(r));
    return parts.join(" ");
}
function pipe(p, indent) {
    let out = "";
    if (p.time)
        out += "time ";
    if (p.negated)
        out += "! ";
    for (let i = 0; i < p.commands.length; i++) {
        if (i > 0)
            out += " " + p.operators[i - 1] + " ";
        out += printNode(p.commands[i], indent);
    }
    return out;
}
function andOr(a, indent) {
    let out = "";
    for (let i = 0; i < a.commands.length; i++) {
        if (i > 0)
            out += " " + a.operators[i - 1] + " ";
        out += printNode(a.commands[i], indent);
    }
    return out;
}
function ifNode(n, indent, isElif) {
    const pad = "  ".repeat(indent);
    const kw = isElif ? "elif" : "if";
    let out = kw + " " + inlineList(n.clause) + "; then\n";
    out += stmts(n.then.commands, indent + 1);
    if (n.else) {
        out += "\n";
        if (n.else.type === "If") {
            out += pad + ifNode(n.else, indent, true);
        }
        else {
            out += pad + "else\n";
            out += stmts(n.else.commands, indent + 1) + "\n";
            out += pad + "fi";
        }
    }
    else {
        out += "\n" + pad + "fi";
    }
    return out;
}
function forNode(n, indent) {
    const pad = "  ".repeat(indent);
    let out = "for " + wd(n.name);
    if (n.wordlist.length > 0) {
        out += " in";
        for (const w of n.wordlist)
            out += " " + wd(w);
    }
    out += "; do\n";
    out += stmts(n.body.commands, indent + 1) + "\n";
    out += pad + "done";
    return out;
}
function whileNode(n, indent) {
    const pad = "  ".repeat(indent);
    let out = n.kind + " " + inlineList(n.clause) + "; do\n";
    out += stmts(n.body.commands, indent + 1) + "\n";
    out += pad + "done";
    return out;
}
function caseNode(n, indent) {
    const pad = "  ".repeat(indent);
    const iPad = "  ".repeat(indent + 1);
    const bPad = "  ".repeat(indent + 2);
    let out = "case " + wd(n.word) + " in\n";
    for (const item of n.items) {
        out += iPad + item.pattern.map((p) => wd(p)).join(" | ") + ")\n";
        if (item.body.commands.length > 0) {
            out += stmts(item.body.commands, indent + 2) + "\n";
        }
        out += bPad + (item.terminator ?? ";;") + "\n";
    }
    out += pad + "esac";
    return out;
}
function funcNode(n, indent) {
    const pad = "  ".repeat(indent);
    let out = wd(n.name) + "() {\n";
    if (n.body.type === "BraceGroup") {
        out += stmts(n.body.body.commands, indent + 1) + "\n";
    }
    else if (n.body.type === "CompoundList") {
        out += stmts(n.body.commands, indent + 1) + "\n";
    }
    else {
        out += "  ".repeat(indent + 1) + printNode(n.body, indent + 1) + "\n";
    }
    out += pad + "}";
    for (const r of n.redirects)
        out += " " + redir(r);
    return out;
}
function subshell(n, indent) {
    const pad = "  ".repeat(indent);
    if (n.body.commands.length <= 1) {
        return "(" + inlineList(n.body) + ")";
    }
    let out = "(\n";
    out += stmts(n.body.commands, indent + 1) + "\n";
    out += pad + ")";
    return out;
}
function braceGroup(n, indent) {
    const pad = "  ".repeat(indent);
    let out = "{\n";
    out += stmts(n.body.commands, indent + 1) + "\n";
    out += pad + "}";
    return out;
}
function testCmd(n) {
    return "[[ " + testExpr(n.expression) + " ]]";
}
function testExpr(e) {
    switch (e.type) {
        case "TestUnary":
            return e.operator + " " + wd(e.operand);
        case "TestBinary":
            return wd(e.left) + " " + e.operator + " " + wd(e.right);
        case "TestLogical":
            return testExpr(e.left) + " " + e.operator + " " + testExpr(e.right);
        case "TestNot":
            return "! " + testExpr(e.operand);
        case "TestGroup":
            return "( " + testExpr(e.expression) + " )";
    }
}
function arithCmd(n) {
    if (n.expression)
        return "(( " + arithExpr(n.expression) + " ))";
    return "((" + n.body + "))";
}
function arithFor(n, indent) {
    const pad = "  ".repeat(indent);
    const init = n.initialize ? arithExpr(n.initialize) : "";
    const test = n.test ? arithExpr(n.test) : "";
    const update = n.update ? arithExpr(n.update) : "";
    let out = "for (( " + init + "; " + test + "; " + update + " )); do\n";
    out += stmts(n.body.commands, indent + 1) + "\n";
    out += pad + "done";
    return out;
}
function coprocNode(n, indent) {
    let out = "coproc";
    if (n.name)
        out += " " + wd(n.name);
    out += " " + printNode(n.body, indent);
    for (const r of n.redirects)
        out += " " + redir(r);
    return out;
}
function arithExpr(e) {
    switch (e.type) {
        case "ArithmeticWord":
            return e.value;
        case "ArithmeticGroup":
            return "(" + arithExpr(e.expression) + ")";
        case "ArithmeticUnary":
            return e.prefix ? e.operator + arithExpr(e.operand) : arithExpr(e.operand) + e.operator;
        case "ArithmeticTernary":
            return arithExpr(e.test) + " ? " + arithExpr(e.consequent) + " : " + arithExpr(e.alternate);
        case "ArithmeticBinary": {
            const prec = arithPrec(e.operator);
            const ra = arithRightAssoc(e.operator);
            let left = arithExpr(e.left);
            let right = arithExpr(e.right);
            if (arithNeedsParens(e.left, prec, ra ? false : true))
                left = "(" + left + ")";
            if (arithNeedsParens(e.right, prec, ra ? true : false))
                right = "(" + right + ")";
            return left + " " + e.operator + " " + right;
        }
        case "ArithmeticCommandExpansion":
            return e.text; // preserve original text for roundtrip
    }
}
function arithPrec(op) {
    switch (op) {
        case ",":
            return 1;
        case "=":
        case "+=":
        case "-=":
        case "*=":
        case "/=":
        case "%=":
        case "<<=":
        case ">>=":
        case "&=":
        case "|=":
        case "^=":
            return 2;
        case "||":
            return 4;
        case "&&":
            return 5;
        case "|":
            return 6;
        case "^":
            return 7;
        case "&":
            return 8;
        case "==":
        case "!=":
            return 9;
        case "<":
        case "<=":
        case ">":
        case ">=":
            return 10;
        case "<<":
        case ">>":
            return 11;
        case "+":
        case "-":
            return 12;
        case "*":
        case "/":
        case "%":
            return 13;
        case "**":
            return 14;
        default:
            return 0;
    }
}
function arithRightAssoc(op) {
    switch (op) {
        case "=":
        case "+=":
        case "-=":
        case "*=":
        case "/=":
        case "%=":
        case "<<=":
        case ">>=":
        case "&=":
        case "|=":
        case "^=":
        case "**":
            return true;
        default:
            return false;
    }
}
function arithNeedsParens(child, parentPrec, isSafe) {
    if (child.type === "ArithmeticBinary") {
        const cp = arithPrec(child.operator);
        return cp < parentPrec || (cp === parentPrec && !isSafe);
    }
    if (child.type === "ArithmeticTernary")
        return 3 < parentPrec;
    return false;
}
function selectNode(n, indent) {
    const pad = "  ".repeat(indent);
    let out = "select " + wd(n.name);
    if (n.wordlist.length > 0) {
        out += " in";
        for (const w of n.wordlist)
            out += " " + wd(w);
    }
    out += "; do\n";
    out += stmts(n.body.commands, indent + 1) + "\n";
    out += pad + "done";
    return out;
}
function redir(r) {
    let out = "";
    if (r.fileDescriptor != null)
        out += r.fileDescriptor;
    if (r.variableName)
        out += "{" + r.variableName + "}";
    out += r.operator;
    if (r.target) {
        if (r.operator !== "<&" && r.operator !== ">&")
            out += " ";
        out += wd(r.target);
    }
    return out;
}
function inlineList(cl) {
    let out = "";
    for (let i = 0; i < cl.commands.length; i++) {
        if (i > 0)
            out += "; ";
        out += inlineStmt(cl.commands[i]);
    }
    return out;
}
function inlineStmt(s) {
    let out = printNode(s.command, 0);
    for (const r of s.redirects)
        out += " " + redir(r);
    if (s.background)
        out += " &";
    return out;
}