'use strict'; const repeatString = require("repeat-string"); const assureArray = require("assure-array"); function debugPrintNode(node) { return `${node.type} | ${node.value || node.operator || node.identifier}`; } function prettyPrint(items, indentation) { if (indentation == null) { throw new Error(`Missing indentation parameter (node: ${debugPrintNode(node)})`); } return items.map((item) => { if (typeof item === "string") { return item.split("\n").map((line) => { return repeatString("\t", indentation) + line; }).join("\n"); } else { return prettyPrint(item, indentation + 1); } }).join("\n"); } function stringifyNode(node, indentation) { if (indentation == null) { throw new Error(`Missing indentation parameter (node: ${debugPrintNode(node)})`); } switch (node.type) { case "operatorExpression": return stringifyOperator(node, indentation); case "numberLiteral": return stringifyNumberLiteral(node, indentation); case "stringLiteral": return stringifyStringLiteral(node, indentation); case "identifier": return stringifyIdentifier(node, indentation); case "set": return stringifySet(node, indentation); case "letBlock": return stringifyLetBlock(node, indentation); case "list": return stringifyList(node, indentation); case "functionDefinition": return stringifyFunctionDefinition(node, indentation); case "group": return stringifyGroup(node, indentation); default: throw new Error(`Unexpected node type '${node.type}'`); } } function stringifyOperator(node, indentation) { let result; switch (node.operator) { case "+": case "-": case "/": case "*": case "++": case "//": case "&&": case "||": case "==": case "!=": case "<": case "<=": case ">": case ">=": result = `${stringifyNode(node.left, indentation)} ${node.operator} ${stringifyNode(node.right, indentation)}`; break; case "call": result = `${stringifyNode(node.left, indentation)} ${stringifyNode(node.right, indentation)}`; break; case "numericalNegate": result = `-${stringifyNode(node.right, indentation)}`; break; case "booleanNegate": result = `!${stringifyNode(node.right, indentation)}`; break; } return `(${result})`; } function stringifyStringLiteral(node, indentation) { return `"${node.value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`; } function stringifyNumberLiteral(node, indentation) { return `${node.value}`; } function stringifyIdentifier(node, indentation) { return `${node.identifier}`; } function stringifyBinding(node, indentation) { return `${stringifyNode(node.identifier, indentation)} = ${stringifyNode(node.expression, indentation)};` } function stringifyGroup(node, indentation) { return `(${stringifyNode(node.expression, indentation)})` } function stringifySet(node, indentation) { return prettyPrint([ "{", node.assignments.map((binding) => { return stringifyBinding(binding, indentation); }), "}" ], indentation); } function stringifyLetBlock(node, indentation) { return prettyPrint([ "let", node.bindings.map((binding) => { return stringifyBinding(binding, indentation); }), "in", [stringifyNode(node.expression, indentation)] ], indentation); } function stringifyList(node, indentation) { return `[ ${node.items.map(item => stringifyNode(item, indentation)).join(" ")} ]`; } function stringifyFunctionDefinition(node, indentation) { return prettyPrint([ `${stringifyNode(node.argument, indentation)}:`, [stringifyNode(node.body, indentation)] ], indentation); } module.exports = function stringifyTree(tree) { return assureArray(tree).map((subTree) => { return stringifyNode(subTree, 0); }).join("\n"); };