UNPKG

glsl-deparser

Version:

through stream that translates glsl-parser AST nodes into working glsl code

493 lines (409 loc) 10.7 kB
module.exports = deparse_stream var through = require('through') , language = require('cssauron-glsl') , 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_stream(with_whitespace, indent) { with_whitespace = with_whitespace === undefined ? true : with_whitespace var stream = through(recv, end) , whitespace = new WSManager(with_whitespace, indent || ' ') stream.parseable = language(':root > *') return stream function recv(node) { if(!stream.parseable(node)) return // reuse the old array. output.length = 0 // reassign the semi-global "ws" ws = whitespace deparse(node) stream.queue(output.join('')) } function end() { stream.queue(null) } } 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 !== null 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]) }