@andrewray/glsl-deparser
Version:
through stream that translates glsl-parser AST nodes into working glsl code
481 lines (402 loc) • 10.5 kB
JavaScript
module.exports = deparse_string;
var WSManager = require("./ws");
var types = {
binary: deparse_binary,
break: deparse_break,
builtin: deparse_builtin,
continue: deparse_continue,
decl: deparse_decl,
decllist: deparse_decllist,
discard: deparse_discard,
"do-while": deparse_do_while,
expr: deparse_expr,
forloop: deparse_forloop,
function: deparse_function,
functionargs: deparse_functionargs,
ident: deparse_ident,
if: deparse_if,
keyword: deparse_keyword,
literal: deparse_literal,
precision: deparse_precision,
preprocessor: deparse_preprocessor,
return: deparse_return,
stmt: deparse_stmt,
stmtlist: deparse_stmtlist,
struct: deparse_struct,
assign: deparse_assign,
unary: deparse_unary,
whileloop: deparse_whileloop,
operator: deparse_operator,
group: deparse_group,
suffix: deparse_suffix,
call: deparse_call,
quantifier: deparse_quantifier,
ternary: deparse_ternary
};
var needs_semicolon = {
decl: true,
return: true,
break: true,
continue: true,
discard: true,
precision: true,
expr: true,
"do-while": true,
struct: true
};
// semi-globals
var output = [],
ws;
function deparse_string(ast, with_whitespace, indent) {
with_whitespace = with_whitespace === undefined ? true : with_whitespace;
var whitespace = new WSManager(with_whitespace, indent || " ");
// reuse the old array.
output.length = 0;
// reassign the semi-global "ws"
ws = whitespace;
deparse(ast);
return output.join("");
}
function deparse(n) {
return types[n.type](n);
}
function deparse_suffix(node) {
deparse(node.children[0]);
output.push(node.data);
}
function deparse_binary(node) {
var is_bracket = node.data === "[";
deparse(node.children[0]);
!is_bracket && output.push(ws.optional(" "));
output.push(node.data);
!is_bracket && output.push(ws.optional(" "));
deparse(node.children[1]);
if (is_bracket) {
output.push("]");
}
}
function deparse_break(node) {
output.push("break");
}
function deparse_builtin(node) {
output.push(node.data);
}
function deparse_continue(node) {
output.push("continue");
}
function deparse_decl(node) {
// it's five long
var len = node.children.length,
len_minus_one = len - 1;
for (var i = 0; i < len; ++i) {
if (node.children[i].type !== "placeholder") {
deparse(node.children[i]);
if (i !== len_minus_one) {
output.push(ws.required(" "));
}
}
}
return;
if (node.children.length === 2) {
deparse(node.children[0]);
output.push(ws.required(" "));
deparse(node.children[1]);
return;
}
if (node.qualified) {
deparse(node.children[0]), output.push(ws.required(" "));
}
deparse(node.children[1]);
output.push(ws.required(" "));
deparse(node.children[2]);
}
function deparse_decllist(node) {
for (var i = 0, len = node.children.length; i < len; ++i) {
if (i > 0) {
if (node.children[i].type !== "ident") {
if (node.children[i].type !== "quantifier") {
output.push(ws.optional(" "));
output.push("=");
output.push(ws.optional(" "));
}
} else {
output.push(",");
output.push(ws.optional(" "));
}
}
deparse(node.children[i]);
}
}
function deparse_discard(node) {
output.push("discard");
}
function deparse_do_while(node) {
var is_stmtlist = node.children[0].type === "stmtlist";
output.push("do");
if (is_stmtlist) {
output.push(ws.optional(" "));
} else {
ws.indent();
output.push(ws.enabled ? ws.optional("\n") : ws.required(" "));
}
deparse(node.children[0]);
if (is_stmtlist) {
output.push(ws.optional(" "));
} else {
ws.dedent();
output.push(ws.optional("\n"));
}
output.push("while(");
deparse(node.children[1]);
output.push(")");
}
function deparse_expr(node) {
node.children.length && deparse(node.children[0]);
}
function deparse_forloop(node) {
var is_stmtlist = node.children[3].type === "stmtlist";
output.push("for(");
deparse(node.children[0]);
output.push(";");
output.push(ws.optional(" "));
deparse(node.children[1]);
output.push(";");
output.push(ws.optional(" "));
deparse(node.children[2]);
output.push(")");
if (is_stmtlist) {
output.push(ws.optional(" "));
} else {
ws.indent();
}
deparse(node.children[3]);
if (!is_stmtlist) {
ws.dedent();
}
}
function deparse_function(node) {
deparse(node.children[0]);
output.push("(");
deparse(node.children[1]);
output.push(")");
if (node.children[2]) {
output.push(ws.optional(" "));
deparse(node.children[2]);
}
}
function deparse_functionargs(node) {
var len = node.children.length,
len_minus_one = len - 1;
for (var i = 0; i < len; ++i) {
deparse(node.children[i]);
if (i !== len_minus_one) {
output.push(",");
output.push(ws.optional(" "));
}
}
}
function deparse_ident(node) {
output.push(node.data);
}
function deparse_if(node) {
var needs_indent = true;
for (var j = 1; j < 4; ++j) {
if (output[output.length - j] === "else") {
output.length = output.length - j;
output.push("else ");
break;
} else if (/[^\s]/.test(output[output.length - j])) {
break;
}
}
var is_first_stmt = node.children[1].type === "stmt",
has_second = node.children[2],
is_second_stmt =
has_second && node.children[2].children[0].type !== "stmtlist";
output.push("if(");
deparse(node.children[0]);
output.push(")");
if (is_first_stmt) {
needs_indent && ws.indent();
output.push(ws.enabled ? ws.optional("\n") : ws.required(" "));
} else {
output.push(ws.optional(" "));
}
deparse(node.children[1]);
if (is_first_stmt) {
needs_indent && ws.dedent();
output.push(ws.optional("\n"));
}
if (has_second) {
var is_if_stmt = node.children[2].children[0].type === "if";
if (output[output.length - 1] === "}") {
output.push(ws.optional(" "));
}
output.push("else");
if (is_second_stmt) {
!is_if_stmt && ws.indent();
output.push(ws.enabled ? ws.optional("\n") : ws.required(" "));
} else {
output.push(ws.optional(" "));
}
deparse(node.children[2]);
if (is_second_stmt) {
!is_if_stmt && ws.dedent();
output.push(ws.optional("\n"));
}
}
}
function deparse_keyword(node) {
output.push(node.token.data);
}
function deparse_literal(node) {
output.push(node.data);
}
function deparse_precision(node) {
var len = node.children.length,
len_minus_one = len - 1;
output.push("precision");
output.push(ws.required(" "));
for (var i = 0; i < len; ++i) {
deparse(node.children[i]);
if (i !== len_minus_one) {
output.push(ws.required(" "));
}
}
}
function deparse_preprocessor(node) {
var level = ws.level;
ws.level = 0;
if (output[output.length - 1] !== "\n") output.push(ws.required("\n"));
output.push(node.token.data);
output.push(ws.required("\n"));
ws.level = level;
}
function deparse_return(node) {
output.push("return");
if (node.children[0]) {
output.push(ws.required(" "));
deparse(node.children[0]);
}
}
function deparse_stmt(node) {
if (!node.children.length) return;
var has_child = node.children.length > 0,
semicolon = has_child ? needs_semicolon[node.children[0].type] : "",
needs_newline = true;
if (has_child && node.children[0].type === "decl") {
if (
node.children[0].children.length > 5 &&
node.children[0].children[5].type === "function"
) {
semicolon = !node.children[0].children[5].children[2];
}
}
if (has_child && node.children[0].type === "stmtlist") {
needs_newline = false;
}
var last = output[output.length - 1];
if (!last || last.charAt(0) !== "\n") {
needs_newline && output.push(ws.optional("\n"));
}
deparse(node.children[0]);
if (semicolon) output.push(";");
}
function deparse_stmtlist(node) {
var has_parent = !!node.parent;
if (has_parent) {
output.push("{");
ws.indent();
output.push(ws.optional("\n"));
}
for (var i = 0, len = node.children.length; i < len; ++i) {
deparse(node.children[i]);
}
if (has_parent) {
ws.dedent();
output.push(ws.optional("\n"));
output.push("}");
}
}
function deparse_struct(node) {
output.push("struct");
output.push(ws.required(" "));
deparse(node.children[0]);
output.push(ws.optional(" "));
output.push("{");
ws.indent();
output.push(ws.optional("\n"));
var len = node.children.length,
len_minus_one = len - 1;
for (var i = 1, len = node.children.length; i < len; ++i) {
deparse(node.children[i]);
if (node.children[i].type !== "preprocessor") {
output.push(";");
}
if (i !== len_minus_one) {
output.push(ws.optional("\n"));
}
}
ws.dedent();
output.push(ws.optional("\n"));
output.push("}");
}
function deparse_assign(node) {
deparse(node.children[0]);
output.push(ws.optional(" "));
output.push(node.token.data);
output.push(ws.optional(" "));
deparse(node.children[1]);
}
function deparse_unary(node) {
output.push(node.data);
deparse(node.children[0]);
}
function deparse_whileloop(node) {
var is_stmtlist = node.children[1].type === "stmtlist";
output.push("while(");
deparse(node.children[0]);
output.push(")");
output.push(is_stmtlist ? ws.optional(" ") : ws.required(" "));
deparse(node.children[1]);
}
function deparse_call(node) {
var len = node.children.length,
len_minus_one = len - 1;
deparse(node.children[0]);
output.push("(");
for (var i = 1; i < len; ++i) {
deparse(node.children[i]);
if (i !== len_minus_one) {
output.push(",");
output.push(ws.optional(" "));
}
}
output.push(")");
}
function deparse_operator(node) {
deparse(node.children[0]);
output.push(node.data);
deparse(node.children[1]);
}
function deparse_group(node) {
output.push("(");
deparse(node.children[0]);
output.push(")");
}
function deparse_quantifier(node) {
output.push("[");
if (node.children[0]) deparse(node.children[0]);
output.push("]");
}
function deparse_ternary(node) {
deparse(node.children[0]);
output.push(ws.optional(" "));
output.push("?");
output.push(ws.optional(" "));
deparse(node.children[1]);
output.push(ws.optional(" "));
output.push(":");
output.push(ws.optional(" "));
deparse(node.children[2]);
}