glsl-deparser
Version:
through stream that translates glsl-parser AST nodes into working glsl code
493 lines (409 loc) • 10.7 kB
JavaScript
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])
}