├── NOTES.md ├── README.md ├── b.js ├── fs.glsl ├── index.html ├── lint ├── lint.js └── unused.js ├── package.json ├── run.js ├── shader_submerged.glsl ├── view.js ├── vs.glsl └── walk.js /NOTES.md: -------------------------------------------------------------------------------- 1 | # References 2 | 3 | - https://www.khronos.org/files/opengles_shading_language.pdf 4 | - https://github.com/stackgl/glsl-tokenizer 5 | - https://github.com/stackgl/glsl-parser 6 | - https://github.com/burg/glsl-simulator/blob/master/lib/compiler/ast.js 7 | 8 | ## Tokens 9 | 10 | - keyword 11 | - identifier 12 | - integer-constant 13 | - floating-constant 14 | - operator 15 | 16 | 17 | ## Comments 18 | /* and */, or by // and a new-lin 19 | 20 | 21 | ## Preprocessor 22 | 23 | ## Variables and Types 24 | all variable functions -> must declare type 25 | type-safe, no implict conversion 26 | structs allowed 27 | 28 | basic_types = [ 29 | void 30 | bool 31 | int 32 | float 33 | vec2 34 | vec3 35 | vec4 36 | bvec2 37 | bvec3 38 | bvec4 39 | ivec2 40 | ivec3 41 | ivec4 42 | mat2 43 | mat3 44 | mat4 45 | sampler2D 46 | samplerCube 47 | ] 48 | 49 | 50 | ## GLSL parser types 51 | 52 | stmtlist - has multiple statements/ preprocessor ["preprocessor", "stmt"] 53 | stmt - Statement. Has precision, ["decl", "return", "if", "expr", "forloop"] 54 | struct - 55 | function - has (ident, functionargs, stmtlist, ) 56 | functionargs - ["decl"] 57 | decl - declaration has ["keyword", "decllist", "function", "placeholder"] 58 | decllist - declaration list. ["ident", "expr"] 59 | forloop ["decl", "expr", "stmt"] 60 | whileloop 61 | if ["expr", "stmtlist", "stmt"] 62 | expr - ["literal", "call", "binary", "group", "ident", "assign", "unary", "operator"] 63 | precision - has Keywords 64 | comment 65 | preprocessor - DEFINE, conditional MACROS (Leaf) 66 | keyword. Keyword. (Leaf Node) 67 | ident - Identifier. (Leaf Node) 68 | return - Return (expr) 69 | continue 70 | break 71 | discard 72 | do-while 73 | binary (binary, call, indent) 74 | ternary - 75 | unary - (call, ident, literal) 76 | call - (keyword, call, literal, unary, builtin, ident, binary, operator) 77 | assign - ["ident", "operator", "binary", "call", "unary", "literal"] 78 | placeholder - does nothing. 79 | 80 | ## GLSL tokenizer types 81 | 82 | block-comment: /* ... */ 83 | line-comment: // ... \n 84 | preprocessor: # ... \n 85 | operator: Any operator. If it looks like punctuation, it's an operator. 86 | float: Optionally suffixed with f 87 | ident: User defined identifier. 88 | builtin: Builtin function. 89 | eof: Emitted on end; data will === '(eof)'. 90 | integer 91 | whitespace 92 | keyword -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GLSL Cleaner 2 | 3 | is a GLSL Code Tree Shaker / Cleaner / Pruner / Dead code eliminator. It parses GLSL code into AST and prune dead code and paths through static analysis. 4 | 5 | # Work In Progress! 6 | 7 | Part I. View + Visualize Tokens that gets emitted by [GLSL-Tokenizer](https://github.com/stackgl/glsl-tokenizer/). This somewhat performs as GLSL Syntax highlighter. 8 | 9 | Part II. Visualize AST after passing tokens through [GLSL-Parser](https://github.com/stackgl/glsl-parser) 10 | 11 | Part III. Analyse AST and scope. (Pontential to build a GLSL-linter here!) 12 | 13 | Part IV. Clean passes: remove unrequired AST nodes. 14 | 15 | Part V. Generate clean GLSL code! 16 | 17 | ## Updates 18 | 19 | 27 Sep 2015 - Simple Linter for unused variables + functions 20 | 22 Sep 2015 - Simple AST / Parse Tree viewer (Part II) 21 | 21 Sep 2015 - Proof of concept quickly done (Part I) 22 | 23 | ## Comments? 24 | Add a github issue/PR, or find me on [twitter](https://twitter.com/blurspline) 25 | 26 | ## TODO 27 | - handle macro/preprocessors/defines 28 | - inline macros in parsetree or token stream? 29 | - refactor to node modules? 30 | - more lint passes 31 | - check type safety 32 | - check declared variables get assigned 33 | - check for redefinations 34 | - mangle AST 35 | - for shorter variable names 36 | - remove dead paths 37 | - make Tree explorer interactive 38 | - write unit tests -------------------------------------------------------------------------------- /b.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o', 47) 56 | infix('>=', 47) 57 | infix('>>', 48) 58 | infix('<<', 48) 59 | infix('+', 50) 60 | infix('-', 50) 61 | infix('*', 60) 62 | infix('/', 60) 63 | infix('%', 60) 64 | infix('?', 20, function(left) { 65 | this.children = [left, expression(0), (advance(':'), expression(0))] 66 | this.type = 'ternary' 67 | return this 68 | }) 69 | infix('.', 80, function(left) { 70 | token.type = 'literal' 71 | state.fake(token) 72 | this.children = [left, token] 73 | advance() 74 | return this 75 | }) 76 | infix('[', 80, function(left) { 77 | this.children = [left, expression(0)] 78 | this.type = 'binary' 79 | advance(']') 80 | return this 81 | }) 82 | infix('(', 80, function(left) { 83 | this.children = [left] 84 | this.type = 'call' 85 | 86 | if(token.data !== ')') while(1) { 87 | this.children.push(expression(0)) 88 | if(token.data !== ',') break 89 | advance(',') 90 | } 91 | advance(')') 92 | return this 93 | }) 94 | 95 | prefix('-') 96 | prefix('+') 97 | prefix('!') 98 | prefix('~') 99 | prefix('defined') 100 | prefix('(', function() { 101 | this.type = 'group' 102 | this.children = [expression(0)] 103 | advance(')') 104 | return this 105 | }) 106 | prefix('++') 107 | prefix('--') 108 | suffix('++') 109 | suffix('--') 110 | 111 | assignment('=') 112 | assignment('+=') 113 | assignment('-=') 114 | assignment('*=') 115 | assignment('/=') 116 | assignment('%=') 117 | assignment('&=') 118 | assignment('|=') 119 | assignment('^=') 120 | assignment('>>=') 121 | assignment('<<=') 122 | 123 | module.exports = function(incoming_state, incoming_tokens) { 124 | state = incoming_state 125 | tokens = incoming_tokens 126 | idx = 0 127 | var result 128 | 129 | if(!tokens.length) return 130 | 131 | advance() 132 | result = expression(0) 133 | result.parent = state[0] 134 | emit(result) 135 | 136 | if(idx < tokens.length) { 137 | throw new Error('did not use all tokens') 138 | } 139 | 140 | result.parent.children = [result] 141 | 142 | function emit(node) { 143 | state.unshift(node, false) 144 | for(var i = 0, len = node.children.length; i < len; ++i) { 145 | emit(node.children[i]) 146 | } 147 | state.shift() 148 | } 149 | 150 | } 151 | 152 | function symbol(id, binding_power) { 153 | var sym = symbol_table[id] 154 | binding_power = binding_power || 0 155 | if(sym) { 156 | if(binding_power > sym.lbp) { 157 | sym.lbp = binding_power 158 | } 159 | } else { 160 | sym = Object.create(original_symbol) 161 | sym.id = id 162 | sym.lbp = binding_power 163 | symbol_table[id] = sym 164 | } 165 | return sym 166 | } 167 | 168 | function expression(rbp) { 169 | var left, t = token 170 | advance() 171 | 172 | left = t.nud() 173 | while(rbp < token.lbp) { 174 | t = token 175 | advance() 176 | left = t.led(left) 177 | } 178 | return left 179 | } 180 | 181 | function infix(id, bp, led) { 182 | var sym = symbol(id, bp) 183 | sym.led = led || function(left) { 184 | this.children = [left, expression(bp)] 185 | this.type = 'binary' 186 | return this 187 | } 188 | } 189 | 190 | function infixr(id, bp, led) { 191 | var sym = symbol(id, bp) 192 | sym.led = led || function(left) { 193 | this.children = [left, expression(bp - 1)] 194 | this.type = 'binary' 195 | return this 196 | } 197 | return sym 198 | } 199 | 200 | function prefix(id, nud) { 201 | var sym = symbol(id) 202 | sym.nud = nud || function() { 203 | this.children = [expression(70)] 204 | this.type = 'unary' 205 | return this 206 | } 207 | return sym 208 | } 209 | 210 | function suffix(id) { 211 | var sym = symbol(id, 150) 212 | sym.led = function(left) { 213 | this.children = [left] 214 | this.type = 'suffix' 215 | return this 216 | } 217 | } 218 | 219 | function assignment(id) { 220 | return infixr(id, 10, function(left) { 221 | this.children = [left, expression(9)] 222 | this.assignment = true 223 | this.type = 'assign' 224 | return this 225 | }) 226 | } 227 | 228 | function advance(id) { 229 | var next 230 | , value 231 | , type 232 | , output 233 | 234 | if(id && token.data !== id) { 235 | return state.unexpected('expected `'+ id + '`, got `'+token.data+'`') 236 | } 237 | 238 | if(idx >= tokens.length) { 239 | token = symbol_table['(end)'] 240 | return 241 | } 242 | 243 | next = tokens[idx++] 244 | value = next.data 245 | type = next.type 246 | 247 | if(type === 'ident') { 248 | output = state.scope.find(value) || state.create_node() 249 | type = output.type 250 | } else if(type === 'builtin') { 251 | output = symbol_table['(builtin)'] 252 | } else if(type === 'keyword') { 253 | output = symbol_table['(keyword)'] 254 | } else if(type === 'operator') { 255 | output = symbol_table[value] 256 | if(!output) { 257 | return state.unexpected('unknown operator `'+value+'`') 258 | } 259 | } else if(type === 'float' || type === 'integer') { 260 | type = 'literal' 261 | output = symbol_table['(literal)'] 262 | } else { 263 | return state.unexpected('unexpected token.') 264 | } 265 | 266 | if(output) { 267 | if(!output.nud) { output.nud = itself } 268 | if(!output.children) { output.children = [] } 269 | } 270 | 271 | output = Object.create(output) 272 | output.token = next 273 | output.type = type 274 | if(!output.data) output.data = value 275 | 276 | return token = output 277 | } 278 | 279 | function fail(message) { 280 | return function() { return state.unexpected(message) } 281 | } 282 | 283 | },{}],3:[function(require,module,exports){ 284 | module.exports = parser 285 | 286 | var full_parse_expr = require('./expr') 287 | , Scope = require('./scope') 288 | 289 | // singleton! 290 | var Advance = new Object 291 | 292 | var DEBUG = false 293 | 294 | var _ = 0 295 | , IDENT = _++ 296 | , STMT = _++ 297 | , STMTLIST = _++ 298 | , STRUCT = _++ 299 | , FUNCTION = _++ 300 | , FUNCTIONARGS = _++ 301 | , DECL = _++ 302 | , DECLLIST = _++ 303 | , FORLOOP = _++ 304 | , WHILELOOP = _++ 305 | , IF = _++ 306 | , EXPR = _++ 307 | , PRECISION = _++ 308 | , COMMENT = _++ 309 | , PREPROCESSOR = _++ 310 | , KEYWORD = _++ 311 | , KEYWORD_OR_IDENT = _++ 312 | , RETURN = _++ 313 | , BREAK = _++ 314 | , CONTINUE = _++ 315 | , DISCARD = _++ 316 | , DOWHILELOOP = _++ 317 | , PLACEHOLDER = _++ 318 | , QUANTIFIER = _++ 319 | 320 | var DECL_ALLOW_ASSIGN = 0x1 321 | , DECL_ALLOW_COMMA = 0x2 322 | , DECL_REQUIRE_NAME = 0x4 323 | , DECL_ALLOW_INVARIANT = 0x8 324 | , DECL_ALLOW_STORAGE = 0x10 325 | , DECL_NO_INOUT = 0x20 326 | , DECL_ALLOW_STRUCT = 0x40 327 | , DECL_STATEMENT = 0xFF 328 | , DECL_FUNCTION = DECL_STATEMENT & ~(DECL_ALLOW_ASSIGN | DECL_ALLOW_COMMA | DECL_NO_INOUT | DECL_ALLOW_INVARIANT | DECL_REQUIRE_NAME) 329 | , DECL_STRUCT = DECL_STATEMENT & ~(DECL_ALLOW_ASSIGN | DECL_ALLOW_INVARIANT | DECL_ALLOW_STORAGE | DECL_ALLOW_STRUCT) 330 | 331 | var QUALIFIERS = ['const', 'attribute', 'uniform', 'varying'] 332 | 333 | var NO_ASSIGN_ALLOWED = false 334 | , NO_COMMA_ALLOWED = false 335 | 336 | // map of tokens to stmt types 337 | var token_map = { 338 | 'block-comment': COMMENT 339 | , 'line-comment': COMMENT 340 | , 'preprocessor': PREPROCESSOR 341 | } 342 | 343 | // map of stmt types to human 344 | var stmt_type = _ = [ 345 | 'ident' 346 | , 'stmt' 347 | , 'stmtlist' 348 | , 'struct' 349 | , 'function' 350 | , 'functionargs' 351 | , 'decl' 352 | , 'decllist' 353 | , 'forloop' 354 | , 'whileloop' 355 | , 'if' 356 | , 'expr' 357 | , 'precision' 358 | , 'comment' 359 | , 'preprocessor' 360 | , 'keyword' 361 | , 'keyword_or_ident' 362 | , 'return' 363 | , 'break' 364 | , 'continue' 365 | , 'discard' 366 | , 'do-while' 367 | , 'placeholder' 368 | , 'quantifier' 369 | ] 370 | 371 | function parser() { 372 | var stmtlist = n(STMTLIST) 373 | , stmt = n(STMT) 374 | , decllist = n(DECLLIST) 375 | , precision = n(PRECISION) 376 | , ident = n(IDENT) 377 | , keyword_or_ident = n(KEYWORD_OR_IDENT) 378 | , fn = n(FUNCTION) 379 | , fnargs = n(FUNCTIONARGS) 380 | , forstmt = n(FORLOOP) 381 | , ifstmt = n(IF) 382 | , whilestmt = n(WHILELOOP) 383 | , returnstmt = n(RETURN) 384 | , dowhilestmt = n(DOWHILELOOP) 385 | , quantifier = n(QUANTIFIER) 386 | 387 | var parse_struct 388 | , parse_precision 389 | , parse_quantifier 390 | , parse_forloop 391 | , parse_if 392 | , parse_return 393 | , parse_whileloop 394 | , parse_dowhileloop 395 | , parse_function 396 | , parse_function_args 397 | 398 | var check = arguments.length ? [].slice.call(arguments) : [] 399 | , complete = false 400 | , ended = false 401 | , depth = 0 402 | , state = [] 403 | , nodes = [] 404 | , tokens = [] 405 | , whitespace = [] 406 | , errored = false 407 | , program 408 | , token 409 | , node 410 | 411 | // setup state 412 | state.shift = special_shift 413 | state.unshift = special_unshift 414 | state.fake = special_fake 415 | state.unexpected = unexpected 416 | state.scope = new Scope(state) 417 | state.create_node = function() { 418 | var n = mknode(IDENT, token) 419 | n.parent = reader.program 420 | return n 421 | } 422 | 423 | setup_stative_parsers() 424 | 425 | // setup root node 426 | node = stmtlist() 427 | node.expecting = '(eof)' 428 | node.mode = STMTLIST 429 | node.token = {type: '(program)', data: '(program)'} 430 | program = node 431 | 432 | reader.program = program 433 | reader.scope = function(scope) { 434 | if(arguments.length === 1) { 435 | state.scope = scope 436 | } 437 | return state.scope 438 | } 439 | 440 | state.unshift(node) 441 | return reader 442 | 443 | function reader(data) { 444 | if (data === null) { 445 | return end(), program 446 | } 447 | 448 | nodes = [] 449 | write(data) 450 | return nodes 451 | } 452 | 453 | // stream functions --------------------------------------------- 454 | 455 | function write(input) { 456 | if(input.type === 'whitespace' || input.type === 'line-comment' || input.type === 'block-comment') { 457 | 458 | whitespace.push(input) 459 | return 460 | } 461 | tokens.push(input) 462 | token = token || tokens[0] 463 | 464 | if(token && whitespace.length) { 465 | token.preceding = token.preceding || [] 466 | token.preceding = token.preceding.concat(whitespace) 467 | whitespace = [] 468 | } 469 | 470 | while(take()) switch(state[0].mode) { 471 | case STMT: parse_stmt(); break 472 | case STMTLIST: parse_stmtlist(); break 473 | case DECL: parse_decl(); break 474 | case DECLLIST: parse_decllist(); break 475 | case EXPR: parse_expr(); break 476 | case STRUCT: parse_struct(true, true); break 477 | case PRECISION: parse_precision(); break 478 | case IDENT: parse_ident(); break 479 | case KEYWORD: parse_keyword(); break 480 | case KEYWORD_OR_IDENT: parse_keyword_or_ident(); break 481 | case FUNCTION: parse_function(); break 482 | case FUNCTIONARGS: parse_function_args(); break 483 | case FORLOOP: parse_forloop(); break 484 | case WHILELOOP: parse_whileloop(); break 485 | case DOWHILELOOP: parse_dowhileloop(); break 486 | case RETURN: parse_return(); break 487 | case IF: parse_if(); break 488 | case QUANTIFIER: parse_quantifier(); break 489 | } 490 | } 491 | 492 | function end(tokens) { 493 | if(arguments.length) { 494 | write(tokens) 495 | } 496 | 497 | if(state.length > 1) { 498 | unexpected('unexpected EOF') 499 | return 500 | } 501 | 502 | complete = true 503 | } 504 | 505 | function take() { 506 | if(errored || !state.length) 507 | return false 508 | 509 | return (token = tokens[0]) 510 | } 511 | 512 | // ----- state manipulation -------- 513 | 514 | function special_fake(x) { 515 | state.unshift(x) 516 | state.shift() 517 | } 518 | 519 | function special_unshift(_node, add_child) { 520 | _node.parent = state[0] 521 | 522 | var ret = [].unshift.call(this, _node) 523 | 524 | add_child = add_child === undefined ? true : add_child 525 | 526 | if(DEBUG) { 527 | var pad = '' 528 | for(var i = 0, len = this.length - 1; i < len; ++i) { 529 | pad += ' |' 530 | } 531 | console.log(pad, '\\'+_node.type, _node.token.data) 532 | } 533 | 534 | if(add_child && node !== _node) node.children.push(_node) 535 | node = _node 536 | 537 | return ret 538 | } 539 | 540 | function special_shift() { 541 | var _node = [].shift.call(this) 542 | , okay = check[this.length] 543 | , emit = false 544 | 545 | if(DEBUG) { 546 | var pad = '' 547 | for(var i = 0, len = this.length; i < len; ++i) { 548 | pad += ' |' 549 | } 550 | console.log(pad, '/'+_node.type) 551 | } 552 | 553 | if(check.length) { 554 | if(typeof check[0] === 'function') { 555 | emit = check[0](_node) 556 | } else if(okay !== undefined) { 557 | emit = okay.test ? okay.test(_node.type) : okay === _node.type 558 | } 559 | } else { 560 | emit = true 561 | } 562 | 563 | if(emit && !errored) nodes.push(_node) 564 | 565 | node = _node.parent 566 | return _node 567 | } 568 | 569 | // parse states --------------- 570 | 571 | function parse_stmtlist() { 572 | // determine the type of the statement 573 | // and then start parsing 574 | return stative( 575 | function() { state.scope.enter(); return Advance } 576 | , normal_mode 577 | )() 578 | 579 | function normal_mode() { 580 | if(token.data === state[0].expecting) { 581 | return state.scope.exit(), state.shift() 582 | } 583 | switch(token.type) { 584 | case 'preprocessor': 585 | state.fake(adhoc()) 586 | tokens.shift() 587 | return 588 | default: 589 | state.unshift(stmt()) 590 | return 591 | } 592 | } 593 | } 594 | 595 | function parse_stmt() { 596 | if(state[0].brace) { 597 | if(token.data !== '}') { 598 | return unexpected('expected `}`, got '+token.data) 599 | } 600 | state[0].brace = false 601 | return tokens.shift(), state.shift() 602 | } 603 | switch(token.type) { 604 | case 'eof': return got_eof() 605 | case 'keyword': 606 | switch(token.data) { 607 | case 'for': return state.unshift(forstmt()); 608 | case 'if': return state.unshift(ifstmt()); 609 | case 'while': return state.unshift(whilestmt()); 610 | case 'do': return state.unshift(dowhilestmt()); 611 | case 'break': return state.fake(mknode(BREAK, token)), tokens.shift() 612 | case 'continue': return state.fake(mknode(CONTINUE, token)), tokens.shift() 613 | case 'discard': return state.fake(mknode(DISCARD, token)), tokens.shift() 614 | case 'return': return state.unshift(returnstmt()); 615 | case 'precision': return state.unshift(precision()); 616 | } 617 | return state.unshift(decl(DECL_STATEMENT)) 618 | case 'ident': 619 | var lookup 620 | if(lookup = state.scope.find(token.data)) { 621 | if(lookup.parent.type === 'struct') { 622 | // this is strictly untrue, you could have an 623 | // expr that starts with a struct constructor. 624 | // ... sigh 625 | return state.unshift(decl(DECL_STATEMENT)) 626 | } 627 | return state.unshift(expr(';')) 628 | } 629 | case 'operator': 630 | if(token.data === '{') { 631 | state[0].brace = true 632 | var n = stmtlist() 633 | n.expecting = '}' 634 | return tokens.shift(), state.unshift(n) 635 | } 636 | if(token.data === ';') { 637 | return tokens.shift(), state.shift() 638 | } 639 | default: return state.unshift(expr(';')) 640 | } 641 | } 642 | 643 | function got_eof() { 644 | if (ended) errored = true 645 | ended = true 646 | return state.shift() 647 | } 648 | 649 | function parse_decl() { 650 | var stmt = state[0] 651 | 652 | return stative( 653 | invariant_or_not, 654 | storage_or_not, 655 | parameter_or_not, 656 | precision_or_not, 657 | struct_or_type, 658 | maybe_name, 659 | maybe_lparen, // lparen means we're a function 660 | is_decllist, 661 | done 662 | )() 663 | 664 | function invariant_or_not() { 665 | if(token.data === 'invariant') { 666 | if(stmt.flags & DECL_ALLOW_INVARIANT) { 667 | state.unshift(keyword()) 668 | return Advance 669 | } else { 670 | return unexpected('`invariant` is not allowed here') 671 | } 672 | } else { 673 | state.fake(mknode(PLACEHOLDER, {data: '', position: token.position})) 674 | return Advance 675 | } 676 | } 677 | 678 | function storage_or_not() { 679 | if(is_storage(token)) { 680 | if(stmt.flags & DECL_ALLOW_STORAGE) { 681 | state.unshift(keyword()) 682 | return Advance 683 | } else { 684 | return unexpected('storage is not allowed here') 685 | } 686 | } else { 687 | state.fake(mknode(PLACEHOLDER, {data: '', position: token.position})) 688 | return Advance 689 | } 690 | } 691 | 692 | function parameter_or_not() { 693 | if(is_parameter(token)) { 694 | if(!(stmt.flags & DECL_NO_INOUT)) { 695 | state.unshift(keyword()) 696 | return Advance 697 | } else { 698 | return unexpected('parameter is not allowed here') 699 | } 700 | } else { 701 | state.fake(mknode(PLACEHOLDER, {data: '', position: token.position})) 702 | return Advance 703 | } 704 | } 705 | 706 | function precision_or_not() { 707 | if(is_precision(token)) { 708 | state.unshift(keyword()) 709 | return Advance 710 | } else { 711 | state.fake(mknode(PLACEHOLDER, {data: '', position: token.position})) 712 | return Advance 713 | } 714 | } 715 | 716 | function struct_or_type() { 717 | if(token.data === 'struct') { 718 | if(!(stmt.flags & DECL_ALLOW_STRUCT)) { 719 | return unexpected('cannot nest structs') 720 | } 721 | state.unshift(struct()) 722 | return Advance 723 | } 724 | 725 | if(token.type === 'keyword') { 726 | state.unshift(keyword()) 727 | return Advance 728 | } 729 | 730 | var lookup = state.scope.find(token.data) 731 | 732 | if(lookup) { 733 | state.fake(Object.create(lookup)) 734 | tokens.shift() 735 | return Advance 736 | } 737 | return unexpected('expected user defined type, struct or keyword, got '+token.data) 738 | } 739 | 740 | function maybe_name() { 741 | if(token.data === ',' && !(stmt.flags & DECL_ALLOW_COMMA)) { 742 | return state.shift() 743 | } 744 | 745 | if(token.data === '[') { 746 | // oh lord. 747 | state.unshift(quantifier()) 748 | return 749 | } 750 | 751 | if(token.data === ')') return state.shift() 752 | 753 | if(token.data === ';') { 754 | return stmt.stage + 3 755 | } 756 | 757 | if(token.type !== 'ident' && token.type !== 'builtin') { 758 | return unexpected('expected identifier, got '+token.data) 759 | } 760 | 761 | stmt.collected_name = tokens.shift() 762 | return Advance 763 | } 764 | 765 | function maybe_lparen() { 766 | if(token.data === '(') { 767 | tokens.unshift(stmt.collected_name) 768 | delete stmt.collected_name 769 | state.unshift(fn()) 770 | return stmt.stage + 2 771 | } 772 | return Advance 773 | } 774 | 775 | function is_decllist() { 776 | tokens.unshift(stmt.collected_name) 777 | delete stmt.collected_name 778 | state.unshift(decllist()) 779 | return Advance 780 | } 781 | 782 | function done() { 783 | return state.shift() 784 | } 785 | } 786 | 787 | function parse_decllist() { 788 | // grab ident 789 | 790 | if(token.type === 'ident') { 791 | var name = token.data 792 | state.unshift(ident()) 793 | state.scope.define(name) 794 | return 795 | } 796 | 797 | if(token.type === 'operator') { 798 | 799 | if(token.data === ',') { 800 | // multi-decl! 801 | if(!(state[1].flags & DECL_ALLOW_COMMA)) { 802 | return state.shift() 803 | } 804 | 805 | return tokens.shift() 806 | } else if(token.data === '=') { 807 | if(!(state[1].flags & DECL_ALLOW_ASSIGN)) return unexpected('`=` is not allowed here.') 808 | 809 | tokens.shift() 810 | 811 | state.unshift(expr(',', ';')) 812 | return 813 | } else if(token.data === '[') { 814 | state.unshift(quantifier()) 815 | return 816 | } 817 | } 818 | return state.shift() 819 | } 820 | 821 | function parse_keyword_or_ident() { 822 | if(token.type === 'keyword') { 823 | state[0].type = 'keyword' 824 | state[0].mode = KEYWORD 825 | return 826 | } 827 | 828 | if(token.type === 'ident') { 829 | state[0].type = 'ident' 830 | state[0].mode = IDENT 831 | return 832 | } 833 | 834 | return unexpected('expected keyword or user-defined name, got '+token.data) 835 | } 836 | 837 | function parse_keyword() { 838 | if(token.type !== 'keyword') { 839 | return unexpected('expected keyword, got '+token.data) 840 | } 841 | 842 | return state.shift(), tokens.shift() 843 | } 844 | 845 | function parse_ident() { 846 | if(token.type !== 'ident') { 847 | return unexpected('expected user-defined name, got '+token.data) 848 | } 849 | 850 | state[0].data = token.data 851 | return state.shift(), tokens.shift() 852 | } 853 | 854 | 855 | function parse_expr() { 856 | var expecting = state[0].expecting 857 | 858 | state[0].tokens = state[0].tokens || [] 859 | 860 | if(state[0].parenlevel === undefined) { 861 | state[0].parenlevel = 0 862 | state[0].bracelevel = 0 863 | } 864 | if(state[0].parenlevel < 1 && expecting.indexOf(token.data) > -1) { 865 | return parseexpr(state[0].tokens) 866 | } 867 | if(token.data === '(') { 868 | ++state[0].parenlevel 869 | } else if(token.data === ')') { 870 | --state[0].parenlevel 871 | } 872 | 873 | switch(token.data) { 874 | case '{': ++state[0].bracelevel; break 875 | case '}': --state[0].bracelevel; break 876 | case '(': ++state[0].parenlevel; break 877 | case ')': --state[0].parenlevel; break 878 | } 879 | 880 | if(state[0].parenlevel < 0) return unexpected('unexpected `)`') 881 | if(state[0].bracelevel < 0) return unexpected('unexpected `}`') 882 | 883 | state[0].tokens.push(tokens.shift()) 884 | return 885 | 886 | function parseexpr(tokens) { 887 | try { 888 | full_parse_expr(state, tokens) 889 | } catch(err) { 890 | errored = true 891 | throw err 892 | } 893 | 894 | return state.shift() 895 | } 896 | } 897 | 898 | // node types --------------- 899 | 900 | function n(type) { 901 | // this is a function factory that suffices for most kinds of expressions and statements 902 | return function() { 903 | return mknode(type, token) 904 | } 905 | } 906 | 907 | function adhoc() { 908 | return mknode(token_map[token.type], token, node) 909 | } 910 | 911 | function decl(flags) { 912 | var _ = mknode(DECL, token, node) 913 | _.flags = flags 914 | 915 | return _ 916 | } 917 | 918 | function struct(allow_assign, allow_comma) { 919 | var _ = mknode(STRUCT, token, node) 920 | _.allow_assign = allow_assign === undefined ? true : allow_assign 921 | _.allow_comma = allow_comma === undefined ? true : allow_comma 922 | return _ 923 | } 924 | 925 | function expr() { 926 | var n = mknode(EXPR, token, node) 927 | 928 | n.expecting = [].slice.call(arguments) 929 | return n 930 | } 931 | 932 | function keyword(default_value) { 933 | var t = token 934 | if(default_value) { 935 | t = {'type': '(implied)', data: '(default)', position: t.position} 936 | } 937 | return mknode(KEYWORD, t, node) 938 | } 939 | 940 | // utils ---------------------------- 941 | 942 | function unexpected(str) { 943 | errored = true 944 | throw new Error( 945 | (str || 'unexpected '+state) + 946 | ' at line '+state[0].token.line 947 | ) 948 | } 949 | 950 | function assert(type, data) { 951 | return 1, 952 | assert_null_string_or_array(type, token.type) && 953 | assert_null_string_or_array(data, token.data) 954 | } 955 | 956 | function assert_null_string_or_array(x, y) { 957 | switch(typeof x) { 958 | case 'string': if(y !== x) { 959 | unexpected('expected `'+x+'`, got '+y+'\n'+token.data); 960 | } return !errored 961 | 962 | case 'object': if(x && x.indexOf(y) === -1) { 963 | unexpected('expected one of `'+x.join('`, `')+'`, got '+y); 964 | } return !errored 965 | } 966 | return true 967 | } 968 | 969 | // stative ---------------------------- 970 | 971 | function stative() { 972 | var steps = [].slice.call(arguments) 973 | , step 974 | , result 975 | 976 | return function() { 977 | var current = state[0] 978 | 979 | current.stage || (current.stage = 0) 980 | 981 | step = steps[current.stage] 982 | if(!step) return unexpected('parser in undefined state!') 983 | 984 | result = step() 985 | 986 | if(result === Advance) return ++current.stage 987 | if(result === undefined) return 988 | current.stage = result 989 | } 990 | } 991 | 992 | function advance(op, t) { 993 | t = t || 'operator' 994 | return function() { 995 | if(!assert(t, op)) return 996 | 997 | var last = tokens.shift() 998 | , children = state[0].children 999 | , last_node = children[children.length - 1] 1000 | 1001 | if(last_node && last_node.token && last.preceding) { 1002 | last_node.token.succeeding = last_node.token.succeeding || [] 1003 | last_node.token.succeeding = last_node.token.succeeding.concat(last.preceding) 1004 | } 1005 | return Advance 1006 | } 1007 | } 1008 | 1009 | function advance_expr(until) { 1010 | return function() { 1011 | state.unshift(expr(until)) 1012 | return Advance 1013 | } 1014 | } 1015 | 1016 | function advance_ident(declare) { 1017 | return declare ? function() { 1018 | var name = token.data 1019 | return assert('ident') && (state.unshift(ident()), state.scope.define(name), Advance) 1020 | } : function() { 1021 | if(!assert('ident')) return 1022 | 1023 | var s = Object.create(state.scope.find(token.data)) 1024 | s.token = token 1025 | 1026 | return (tokens.shift(), Advance) 1027 | } 1028 | } 1029 | 1030 | function advance_stmtlist() { 1031 | return function() { 1032 | var n = stmtlist() 1033 | n.expecting = '}' 1034 | return state.unshift(n), Advance 1035 | } 1036 | } 1037 | 1038 | function maybe_stmtlist(skip) { 1039 | return function() { 1040 | var current = state[0].stage 1041 | if(token.data !== '{') { return state.unshift(stmt()), current + skip } 1042 | return tokens.shift(), Advance 1043 | } 1044 | } 1045 | 1046 | function popstmt() { 1047 | return function() { return state.shift(), state.shift() } 1048 | } 1049 | 1050 | 1051 | function setup_stative_parsers() { 1052 | 1053 | // could also be 1054 | // struct { } decllist 1055 | parse_struct = 1056 | stative( 1057 | advance('struct', 'keyword') 1058 | , function() { 1059 | if(token.data === '{') { 1060 | state.fake(mknode(IDENT, {data:'', position: token.position, type:'ident'})) 1061 | return Advance 1062 | } 1063 | 1064 | return advance_ident(true)() 1065 | } 1066 | , function() { state.scope.enter(); return Advance } 1067 | , advance('{') 1068 | , function() { 1069 | if(token.type === 'preprocessor') { 1070 | state.fake(adhoc()) 1071 | tokens.shift() 1072 | return 1073 | } 1074 | if(token.data === '}') { 1075 | state.scope.exit() 1076 | tokens.shift() 1077 | return state.shift() 1078 | } 1079 | if(token.data === ';') { tokens.shift(); return } 1080 | state.unshift(decl(DECL_STRUCT)) 1081 | } 1082 | ) 1083 | 1084 | parse_precision = 1085 | stative( 1086 | function() { return tokens.shift(), Advance } 1087 | , function() { 1088 | return assert( 1089 | 'keyword', ['lowp', 'mediump', 'highp'] 1090 | ) && (state.unshift(keyword()), Advance) 1091 | } 1092 | , function() { return (state.unshift(keyword()), Advance) } 1093 | , function() { return state.shift() } 1094 | ) 1095 | 1096 | parse_quantifier = 1097 | stative( 1098 | advance('[') 1099 | , advance_expr(']') 1100 | , advance(']') 1101 | , function() { return state.shift() } 1102 | ) 1103 | 1104 | parse_forloop = 1105 | stative( 1106 | advance('for', 'keyword') 1107 | , advance('(') 1108 | , function() { 1109 | var lookup 1110 | if(token.type === 'ident') { 1111 | if(!(lookup = state.scope.find(token.data))) { 1112 | lookup = state.create_node() 1113 | } 1114 | 1115 | if(lookup.parent.type === 'struct') { 1116 | return state.unshift(decl(DECL_STATEMENT)), Advance 1117 | } 1118 | } else if(token.type === 'builtin' || token.type === 'keyword') { 1119 | return state.unshift(decl(DECL_STATEMENT)), Advance 1120 | } 1121 | return advance_expr(';')() 1122 | } 1123 | , advance(';') 1124 | , advance_expr(';') 1125 | , advance(';') 1126 | , advance_expr(')') 1127 | , advance(')') 1128 | , maybe_stmtlist(3) 1129 | , advance_stmtlist() 1130 | , advance('}') 1131 | , popstmt() 1132 | ) 1133 | 1134 | parse_if = 1135 | stative( 1136 | advance('if', 'keyword') 1137 | , advance('(') 1138 | , advance_expr(')') 1139 | , advance(')') 1140 | , maybe_stmtlist(3) 1141 | , advance_stmtlist() 1142 | , advance('}') 1143 | , function() { 1144 | if(token.data === 'else') { 1145 | return tokens.shift(), state.unshift(stmt()), Advance 1146 | } 1147 | return popstmt()() 1148 | } 1149 | , popstmt() 1150 | ) 1151 | 1152 | parse_return = 1153 | stative( 1154 | advance('return', 'keyword') 1155 | , function() { 1156 | if(token.data === ';') return Advance 1157 | return state.unshift(expr(';')), Advance 1158 | } 1159 | , function() { tokens.shift(), popstmt()() } 1160 | ) 1161 | 1162 | parse_whileloop = 1163 | stative( 1164 | advance('while', 'keyword') 1165 | , advance('(') 1166 | , advance_expr(')') 1167 | , advance(')') 1168 | , maybe_stmtlist(3) 1169 | , advance_stmtlist() 1170 | , advance('}') 1171 | , popstmt() 1172 | ) 1173 | 1174 | parse_dowhileloop = 1175 | stative( 1176 | advance('do', 'keyword') 1177 | , maybe_stmtlist(3) 1178 | , advance_stmtlist() 1179 | , advance('}') 1180 | , advance('while', 'keyword') 1181 | , advance('(') 1182 | , advance_expr(')') 1183 | , advance(')') 1184 | , popstmt() 1185 | ) 1186 | 1187 | parse_function = 1188 | stative( 1189 | function() { 1190 | for(var i = 1, len = state.length; i < len; ++i) if(state[i].mode === FUNCTION) { 1191 | return unexpected('function definition is not allowed within another function') 1192 | } 1193 | 1194 | return Advance 1195 | } 1196 | , function() { 1197 | if(!assert("ident")) return 1198 | 1199 | var name = token.data 1200 | , lookup = state.scope.find(name) 1201 | 1202 | state.unshift(ident()) 1203 | state.scope.define(name) 1204 | 1205 | state.scope.enter(lookup ? lookup.scope : null) 1206 | return Advance 1207 | } 1208 | , advance('(') 1209 | , function() { return state.unshift(fnargs()), Advance } 1210 | , advance(')') 1211 | , function() { 1212 | // forward decl 1213 | if(token.data === ';') { 1214 | return state.scope.exit(), state.shift(), state.shift() 1215 | } 1216 | return Advance 1217 | } 1218 | , advance('{') 1219 | , advance_stmtlist() 1220 | , advance('}') 1221 | , function() { state.scope.exit(); return Advance } 1222 | , function() { return state.shift(), state.shift(), state.shift() } 1223 | ) 1224 | 1225 | parse_function_args = 1226 | stative( 1227 | function() { 1228 | if(token.data === 'void') { state.fake(keyword()); tokens.shift(); return Advance } 1229 | if(token.data === ')') { state.shift(); return } 1230 | if(token.data === 'struct') { 1231 | state.unshift(struct(NO_ASSIGN_ALLOWED, NO_COMMA_ALLOWED)) 1232 | return Advance 1233 | } 1234 | state.unshift(decl(DECL_FUNCTION)) 1235 | return Advance 1236 | } 1237 | , function() { 1238 | if(token.data === ',') { tokens.shift(); return 0 } 1239 | if(token.data === ')') { state.shift(); return } 1240 | unexpected('expected one of `,` or `)`, got '+token.data) 1241 | } 1242 | ) 1243 | } 1244 | } 1245 | 1246 | function mknode(mode, sourcetoken) { 1247 | return { 1248 | mode: mode 1249 | , token: sourcetoken 1250 | , children: [] 1251 | , type: stmt_type[mode] 1252 | , id: (Math.random() * 0xFFFFFFFF).toString(16) 1253 | } 1254 | } 1255 | 1256 | function is_storage(token) { 1257 | return token.data === 'const' || 1258 | token.data === 'attribute' || 1259 | token.data === 'uniform' || 1260 | token.data === 'varying' 1261 | } 1262 | 1263 | function is_parameter(token) { 1264 | return token.data === 'in' || 1265 | token.data === 'inout' || 1266 | token.data === 'out' 1267 | } 1268 | 1269 | function is_precision(token) { 1270 | return token.data === 'highp' || 1271 | token.data === 'mediump' || 1272 | token.data === 'lowp' 1273 | } 1274 | 1275 | },{"./expr":2,"./scope":4}],4:[function(require,module,exports){ 1276 | module.exports = scope 1277 | 1278 | function scope(state) { 1279 | if(this.constructor !== scope) 1280 | return new scope(state) 1281 | 1282 | this.state = state 1283 | this.scopes = [] 1284 | this.current = null 1285 | } 1286 | 1287 | var cons = scope 1288 | , proto = cons.prototype 1289 | 1290 | proto.enter = function(s) { 1291 | this.scopes.push( 1292 | this.current = this.state[0].scope = s || {} 1293 | ) 1294 | } 1295 | 1296 | proto.exit = function() { 1297 | this.scopes.pop() 1298 | this.current = this.scopes[this.scopes.length - 1] 1299 | } 1300 | 1301 | proto.define = function(str) { 1302 | this.current[str] = this.state[0] 1303 | } 1304 | 1305 | proto.find = function(name, fail) { 1306 | for(var i = this.scopes.length - 1; i > -1; --i) { 1307 | if(this.scopes[i].hasOwnProperty(name)) { 1308 | return this.scopes[i][name] 1309 | } 1310 | } 1311 | 1312 | return null 1313 | } 1314 | 1315 | },{}],5:[function(require,module,exports){ 1316 | module.exports = tokenize 1317 | 1318 | var literals = require('./lib/literals') 1319 | , operators = require('./lib/operators') 1320 | , builtins = require('./lib/builtins') 1321 | 1322 | var NORMAL = 999 // <-- never emitted 1323 | , TOKEN = 9999 // <-- never emitted 1324 | , BLOCK_COMMENT = 0 1325 | , LINE_COMMENT = 1 1326 | , PREPROCESSOR = 2 1327 | , OPERATOR = 3 1328 | , INTEGER = 4 1329 | , FLOAT = 5 1330 | , IDENT = 6 1331 | , BUILTIN = 7 1332 | , KEYWORD = 8 1333 | , WHITESPACE = 9 1334 | , EOF = 10 1335 | , HEX = 11 1336 | 1337 | var map = [ 1338 | 'block-comment' 1339 | , 'line-comment' 1340 | , 'preprocessor' 1341 | , 'operator' 1342 | , 'integer' 1343 | , 'float' 1344 | , 'ident' 1345 | , 'builtin' 1346 | , 'keyword' 1347 | , 'whitespace' 1348 | , 'eof' 1349 | , 'integer' 1350 | ] 1351 | 1352 | function tokenize() { 1353 | var i = 0 1354 | , total = 0 1355 | , mode = NORMAL 1356 | , c 1357 | , last 1358 | , content = [] 1359 | , tokens = [] 1360 | , token_idx = 0 1361 | , token_offs = 0 1362 | , line = 1 1363 | , col = 0 1364 | , start = 0 1365 | , isnum = false 1366 | , isoperator = false 1367 | , input = '' 1368 | , len 1369 | 1370 | return function(data) { 1371 | tokens = [] 1372 | if (data !== null) return write(data) 1373 | return end() 1374 | } 1375 | 1376 | function token(data) { 1377 | if (data.length) { 1378 | tokens.push({ 1379 | type: map[mode] 1380 | , data: data 1381 | , position: start 1382 | , line: line 1383 | , column: col 1384 | }) 1385 | } 1386 | } 1387 | 1388 | function write(chunk) { 1389 | i = 0 1390 | input += chunk 1391 | len = input.length 1392 | 1393 | var last 1394 | 1395 | while(c = input[i], i < len) { 1396 | last = i 1397 | 1398 | switch(mode) { 1399 | case BLOCK_COMMENT: i = block_comment(); break 1400 | case LINE_COMMENT: i = line_comment(); break 1401 | case PREPROCESSOR: i = preprocessor(); break 1402 | case OPERATOR: i = operator(); break 1403 | case INTEGER: i = integer(); break 1404 | case HEX: i = hex(); break 1405 | case FLOAT: i = decimal(); break 1406 | case TOKEN: i = readtoken(); break 1407 | case WHITESPACE: i = whitespace(); break 1408 | case NORMAL: i = normal(); break 1409 | } 1410 | 1411 | if(last !== i) { 1412 | switch(input[last]) { 1413 | case '\n': col = 0; ++line; break 1414 | default: ++col; break 1415 | } 1416 | } 1417 | } 1418 | 1419 | total += i 1420 | input = input.slice(i) 1421 | return tokens 1422 | } 1423 | 1424 | function end(chunk) { 1425 | if(content.length) { 1426 | token(content.join('')) 1427 | } 1428 | 1429 | mode = EOF 1430 | token('(eof)') 1431 | return tokens 1432 | } 1433 | 1434 | function normal() { 1435 | content = content.length ? [] : content 1436 | 1437 | if(last === '/' && c === '*') { 1438 | start = total + i - 1 1439 | mode = BLOCK_COMMENT 1440 | last = c 1441 | return i + 1 1442 | } 1443 | 1444 | if(last === '/' && c === '/') { 1445 | start = total + i - 1 1446 | mode = LINE_COMMENT 1447 | last = c 1448 | return i + 1 1449 | } 1450 | 1451 | if(c === '#') { 1452 | mode = PREPROCESSOR 1453 | start = total + i 1454 | return i 1455 | } 1456 | 1457 | if(/\s/.test(c)) { 1458 | mode = WHITESPACE 1459 | start = total + i 1460 | return i 1461 | } 1462 | 1463 | isnum = /\d/.test(c) 1464 | isoperator = /[^\w_]/.test(c) 1465 | 1466 | start = total + i 1467 | mode = isnum ? INTEGER : isoperator ? OPERATOR : TOKEN 1468 | return i 1469 | } 1470 | 1471 | function whitespace() { 1472 | if(/[^\s]/g.test(c)) { 1473 | token(content.join('')) 1474 | mode = NORMAL 1475 | return i 1476 | } 1477 | content.push(c) 1478 | last = c 1479 | return i + 1 1480 | } 1481 | 1482 | function preprocessor() { 1483 | if(c === '\n' && last !== '\\') { 1484 | token(content.join('')) 1485 | mode = NORMAL 1486 | return i 1487 | } 1488 | content.push(c) 1489 | last = c 1490 | return i + 1 1491 | } 1492 | 1493 | function line_comment() { 1494 | return preprocessor() 1495 | } 1496 | 1497 | function block_comment() { 1498 | if(c === '/' && last === '*') { 1499 | content.push(c) 1500 | token(content.join('')) 1501 | mode = NORMAL 1502 | return i + 1 1503 | } 1504 | 1505 | content.push(c) 1506 | last = c 1507 | return i + 1 1508 | } 1509 | 1510 | function operator() { 1511 | if(last === '.' && /\d/.test(c)) { 1512 | mode = FLOAT 1513 | return i 1514 | } 1515 | 1516 | if(last === '/' && c === '*') { 1517 | mode = BLOCK_COMMENT 1518 | return i 1519 | } 1520 | 1521 | if(last === '/' && c === '/') { 1522 | mode = LINE_COMMENT 1523 | return i 1524 | } 1525 | 1526 | if(c === '.' && content.length) { 1527 | while(determine_operator(content)); 1528 | 1529 | mode = FLOAT 1530 | return i 1531 | } 1532 | 1533 | if(c === ';' || c === ')' || c === '(') { 1534 | if(content.length) while(determine_operator(content)); 1535 | token(c) 1536 | mode = NORMAL 1537 | return i + 1 1538 | } 1539 | 1540 | var is_composite_operator = content.length === 2 && c !== '=' 1541 | if(/[\w_\d\s]/.test(c) || is_composite_operator) { 1542 | while(determine_operator(content)); 1543 | mode = NORMAL 1544 | return i 1545 | } 1546 | 1547 | content.push(c) 1548 | last = c 1549 | return i + 1 1550 | } 1551 | 1552 | function determine_operator(buf) { 1553 | var j = 0 1554 | , idx 1555 | , res 1556 | 1557 | do { 1558 | idx = operators.indexOf(buf.slice(0, buf.length + j).join('')) 1559 | res = operators[idx] 1560 | 1561 | if(idx === -1) { 1562 | if(j-- + buf.length > 0) continue 1563 | res = buf.slice(0, 1).join('') 1564 | } 1565 | 1566 | token(res) 1567 | 1568 | start += res.length 1569 | content = content.slice(res.length) 1570 | return content.length 1571 | } while(1) 1572 | } 1573 | 1574 | function hex() { 1575 | if(/[^a-fA-F0-9]/.test(c)) { 1576 | token(content.join('')) 1577 | mode = NORMAL 1578 | return i 1579 | } 1580 | 1581 | content.push(c) 1582 | last = c 1583 | return i + 1 1584 | } 1585 | 1586 | function integer() { 1587 | if(c === '.') { 1588 | content.push(c) 1589 | mode = FLOAT 1590 | last = c 1591 | return i + 1 1592 | } 1593 | 1594 | if(/[eE]/.test(c)) { 1595 | content.push(c) 1596 | mode = FLOAT 1597 | last = c 1598 | return i + 1 1599 | } 1600 | 1601 | if(c === 'x' && content.length === 1 && content[0] === '0') { 1602 | mode = HEX 1603 | content.push(c) 1604 | last = c 1605 | return i + 1 1606 | } 1607 | 1608 | if(/[^\d]/.test(c)) { 1609 | token(content.join('')) 1610 | mode = NORMAL 1611 | return i 1612 | } 1613 | 1614 | content.push(c) 1615 | last = c 1616 | return i + 1 1617 | } 1618 | 1619 | function decimal() { 1620 | if(c === 'f') { 1621 | content.push(c) 1622 | last = c 1623 | i += 1 1624 | } 1625 | 1626 | if(/[eE]/.test(c)) { 1627 | content.push(c) 1628 | last = c 1629 | return i + 1 1630 | } 1631 | 1632 | if(/[^\d]/.test(c)) { 1633 | token(content.join('')) 1634 | mode = NORMAL 1635 | return i 1636 | } 1637 | content.push(c) 1638 | last = c 1639 | return i + 1 1640 | } 1641 | 1642 | function readtoken() { 1643 | if(/[^\d\w_]/.test(c)) { 1644 | var contentstr = content.join('') 1645 | if(literals.indexOf(contentstr) > -1) { 1646 | mode = KEYWORD 1647 | } else if(builtins.indexOf(contentstr) > -1) { 1648 | mode = BUILTIN 1649 | } else { 1650 | mode = IDENT 1651 | } 1652 | token(content.join('')) 1653 | mode = NORMAL 1654 | return i 1655 | } 1656 | content.push(c) 1657 | last = c 1658 | return i + 1 1659 | } 1660 | } 1661 | 1662 | },{"./lib/builtins":6,"./lib/literals":7,"./lib/operators":8}],6:[function(require,module,exports){ 1663 | module.exports = [ 1664 | 'gl_Position' 1665 | , 'gl_PointSize' 1666 | , 'gl_ClipVertex' 1667 | , 'gl_FragCoord' 1668 | , 'gl_FrontFacing' 1669 | , 'gl_FragColor' 1670 | , 'gl_FragData' 1671 | , 'gl_FragDepth' 1672 | , 'gl_Color' 1673 | , 'gl_SecondaryColor' 1674 | , 'gl_Normal' 1675 | , 'gl_Vertex' 1676 | , 'gl_MultiTexCoord0' 1677 | , 'gl_MultiTexCoord1' 1678 | , 'gl_MultiTexCoord2' 1679 | , 'gl_MultiTexCoord3' 1680 | , 'gl_MultiTexCoord4' 1681 | , 'gl_MultiTexCoord5' 1682 | , 'gl_MultiTexCoord6' 1683 | , 'gl_MultiTexCoord7' 1684 | , 'gl_FogCoord' 1685 | , 'gl_MaxLights' 1686 | , 'gl_MaxClipPlanes' 1687 | , 'gl_MaxTextureUnits' 1688 | , 'gl_MaxTextureCoords' 1689 | , 'gl_MaxVertexAttribs' 1690 | , 'gl_MaxVertexUniformComponents' 1691 | , 'gl_MaxVaryingFloats' 1692 | , 'gl_MaxVertexTextureImageUnits' 1693 | , 'gl_MaxCombinedTextureImageUnits' 1694 | , 'gl_MaxTextureImageUnits' 1695 | , 'gl_MaxFragmentUniformComponents' 1696 | , 'gl_MaxDrawBuffers' 1697 | , 'gl_ModelViewMatrix' 1698 | , 'gl_ProjectionMatrix' 1699 | , 'gl_ModelViewProjectionMatrix' 1700 | , 'gl_TextureMatrix' 1701 | , 'gl_NormalMatrix' 1702 | , 'gl_ModelViewMatrixInverse' 1703 | , 'gl_ProjectionMatrixInverse' 1704 | , 'gl_ModelViewProjectionMatrixInverse' 1705 | , 'gl_TextureMatrixInverse' 1706 | , 'gl_ModelViewMatrixTranspose' 1707 | , 'gl_ProjectionMatrixTranspose' 1708 | , 'gl_ModelViewProjectionMatrixTranspose' 1709 | , 'gl_TextureMatrixTranspose' 1710 | , 'gl_ModelViewMatrixInverseTranspose' 1711 | , 'gl_ProjectionMatrixInverseTranspose' 1712 | , 'gl_ModelViewProjectionMatrixInverseTranspose' 1713 | , 'gl_TextureMatrixInverseTranspose' 1714 | , 'gl_NormalScale' 1715 | , 'gl_DepthRangeParameters' 1716 | , 'gl_DepthRange' 1717 | , 'gl_ClipPlane' 1718 | , 'gl_PointParameters' 1719 | , 'gl_Point' 1720 | , 'gl_MaterialParameters' 1721 | , 'gl_FrontMaterial' 1722 | , 'gl_BackMaterial' 1723 | , 'gl_LightSourceParameters' 1724 | , 'gl_LightSource' 1725 | , 'gl_LightModelParameters' 1726 | , 'gl_LightModel' 1727 | , 'gl_LightModelProducts' 1728 | , 'gl_FrontLightModelProduct' 1729 | , 'gl_BackLightModelProduct' 1730 | , 'gl_LightProducts' 1731 | , 'gl_FrontLightProduct' 1732 | , 'gl_BackLightProduct' 1733 | , 'gl_FogParameters' 1734 | , 'gl_Fog' 1735 | , 'gl_TextureEnvColor' 1736 | , 'gl_EyePlaneS' 1737 | , 'gl_EyePlaneT' 1738 | , 'gl_EyePlaneR' 1739 | , 'gl_EyePlaneQ' 1740 | , 'gl_ObjectPlaneS' 1741 | , 'gl_ObjectPlaneT' 1742 | , 'gl_ObjectPlaneR' 1743 | , 'gl_ObjectPlaneQ' 1744 | , 'gl_FrontColor' 1745 | , 'gl_BackColor' 1746 | , 'gl_FrontSecondaryColor' 1747 | , 'gl_BackSecondaryColor' 1748 | , 'gl_TexCoord' 1749 | , 'gl_FogFragCoord' 1750 | , 'gl_Color' 1751 | , 'gl_SecondaryColor' 1752 | , 'gl_TexCoord' 1753 | , 'gl_FogFragCoord' 1754 | , 'gl_PointCoord' 1755 | , 'radians' 1756 | , 'degrees' 1757 | , 'sin' 1758 | , 'cos' 1759 | , 'tan' 1760 | , 'asin' 1761 | , 'acos' 1762 | , 'atan' 1763 | , 'pow' 1764 | , 'exp' 1765 | , 'log' 1766 | , 'exp2' 1767 | , 'log2' 1768 | , 'sqrt' 1769 | , 'inversesqrt' 1770 | , 'abs' 1771 | , 'sign' 1772 | , 'floor' 1773 | , 'ceil' 1774 | , 'fract' 1775 | , 'mod' 1776 | , 'min' 1777 | , 'max' 1778 | , 'clamp' 1779 | , 'mix' 1780 | , 'step' 1781 | , 'smoothstep' 1782 | , 'length' 1783 | , 'distance' 1784 | , 'dot' 1785 | , 'cross' 1786 | , 'normalize' 1787 | , 'faceforward' 1788 | , 'reflect' 1789 | , 'refract' 1790 | , 'matrixCompMult' 1791 | , 'lessThan' 1792 | , 'lessThanEqual' 1793 | , 'greaterThan' 1794 | , 'greaterThanEqual' 1795 | , 'equal' 1796 | , 'notEqual' 1797 | , 'any' 1798 | , 'all' 1799 | , 'not' 1800 | , 'texture2D' 1801 | , 'texture2DProj' 1802 | , 'texture2DLod' 1803 | , 'texture2DProjLod' 1804 | , 'textureCube' 1805 | , 'textureCubeLod' 1806 | , 'dFdx' 1807 | , 'dFdy' 1808 | ] 1809 | 1810 | },{}],7:[function(require,module,exports){ 1811 | module.exports = [ 1812 | // current 1813 | 'precision' 1814 | , 'highp' 1815 | , 'mediump' 1816 | , 'lowp' 1817 | , 'attribute' 1818 | , 'const' 1819 | , 'uniform' 1820 | , 'varying' 1821 | , 'break' 1822 | , 'continue' 1823 | , 'do' 1824 | , 'for' 1825 | , 'while' 1826 | , 'if' 1827 | , 'else' 1828 | , 'in' 1829 | , 'out' 1830 | , 'inout' 1831 | , 'float' 1832 | , 'int' 1833 | , 'void' 1834 | , 'bool' 1835 | , 'true' 1836 | , 'false' 1837 | , 'discard' 1838 | , 'return' 1839 | , 'mat2' 1840 | , 'mat3' 1841 | , 'mat4' 1842 | , 'vec2' 1843 | , 'vec3' 1844 | , 'vec4' 1845 | , 'ivec2' 1846 | , 'ivec3' 1847 | , 'ivec4' 1848 | , 'bvec2' 1849 | , 'bvec3' 1850 | , 'bvec4' 1851 | , 'sampler1D' 1852 | , 'sampler2D' 1853 | , 'sampler3D' 1854 | , 'samplerCube' 1855 | , 'sampler1DShadow' 1856 | , 'sampler2DShadow' 1857 | , 'struct' 1858 | 1859 | // future 1860 | , 'asm' 1861 | , 'class' 1862 | , 'union' 1863 | , 'enum' 1864 | , 'typedef' 1865 | , 'template' 1866 | , 'this' 1867 | , 'packed' 1868 | , 'goto' 1869 | , 'switch' 1870 | , 'default' 1871 | , 'inline' 1872 | , 'noinline' 1873 | , 'volatile' 1874 | , 'public' 1875 | , 'static' 1876 | , 'extern' 1877 | , 'external' 1878 | , 'interface' 1879 | , 'long' 1880 | , 'short' 1881 | , 'double' 1882 | , 'half' 1883 | , 'fixed' 1884 | , 'unsigned' 1885 | , 'input' 1886 | , 'output' 1887 | , 'hvec2' 1888 | , 'hvec3' 1889 | , 'hvec4' 1890 | , 'dvec2' 1891 | , 'dvec3' 1892 | , 'dvec4' 1893 | , 'fvec2' 1894 | , 'fvec3' 1895 | , 'fvec4' 1896 | , 'sampler2DRect' 1897 | , 'sampler3DRect' 1898 | , 'sampler2DRectShadow' 1899 | , 'sizeof' 1900 | , 'cast' 1901 | , 'namespace' 1902 | , 'using' 1903 | ] 1904 | 1905 | },{}],8:[function(require,module,exports){ 1906 | module.exports = [ 1907 | '<<=' 1908 | , '>>=' 1909 | , '++' 1910 | , '--' 1911 | , '<<' 1912 | , '>>' 1913 | , '<=' 1914 | , '>=' 1915 | , '==' 1916 | , '!=' 1917 | , '&&' 1918 | , '||' 1919 | , '+=' 1920 | , '-=' 1921 | , '*=' 1922 | , '/=' 1923 | , '%=' 1924 | , '&=' 1925 | , '^^' 1926 | , '^=' 1927 | , '|=' 1928 | , '(' 1929 | , ')' 1930 | , '[' 1931 | , ']' 1932 | , '.' 1933 | , '!' 1934 | , '~' 1935 | , '*' 1936 | , '/' 1937 | , '%' 1938 | , '+' 1939 | , '-' 1940 | , '<' 1941 | , '>' 1942 | , '&' 1943 | , '^' 1944 | , '|' 1945 | , '?' 1946 | , ':' 1947 | , '=' 1948 | , ',' 1949 | , ';' 1950 | , '{' 1951 | , '}' 1952 | ] 1953 | 1954 | },{}],9:[function(require,module,exports){ 1955 | var tokenize = require('./index') 1956 | 1957 | module.exports = tokenizeString 1958 | 1959 | function tokenizeString(str) { 1960 | var generator = tokenize() 1961 | var tokens = [] 1962 | 1963 | tokens = tokens.concat(generator(str)) 1964 | tokens = tokens.concat(generator(null)) 1965 | 1966 | return tokens 1967 | } 1968 | 1969 | },{"./index":5}],10:[function(require,module,exports){ 1970 | var TokenString = require('./glsl-tokenizer/string') 1971 | var ParseTokens = require('./glsl-parser/direct') 1972 | 1973 | window.process = process; 1974 | window.tokenize = TokenString; 1975 | window.parse = ParseTokens; 1976 | 1977 | function process(code) { 1978 | tokens = TokenString(code) 1979 | ast = ParseTokens(tokens) 1980 | console.log(JSON.stringify(tokens)) 1981 | } 1982 | },{"./glsl-parser/direct":1,"./glsl-tokenizer/string":9}]},{},[10]) -------------------------------------------------------------------------------- /fs.glsl: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | precision highp int; 3 | #define SHADER_NAME ShaderMaterial 4 | #define MAX_DIR_LIGHTS 0 5 | #define MAX_POINT_LIGHTS 0 6 | #define MAX_SPOT_LIGHTS 0 7 | #define MAX_HEMI_LIGHTS 0 8 | #define MAX_SHADOWS 0 9 | #define GAMMA_FACTOR 2 10 | #define FLIP_SIDED 11 | uniform mat4 viewMatrix; 12 | uniform vec3 cameraPosition; 13 | 14 | uniform sampler2D skySampler; 15 | uniform vec3 sunPosition; 16 | varying vec3 vWorldPosition; 17 | vec3 cameraPos = vec3(0., 0., 0.); 18 | // uniform sampler2D sDiffuse; 19 | // const float turbidity = 10.0; // 20 | // const float reileigh = 2.; // 21 | // const float luminance = 1.0; // 22 | // const float mieCoefficient = 0.005; 23 | // const float mieDirectionalG = 0.8; 24 | uniform float luminance; 25 | uniform float turbidity; 26 | uniform float reileigh; 27 | uniform float mieCoefficient; 28 | uniform float mieDirectionalG; 29 | // constants for atmospheric scattering 30 | const float e = 2.71828182845904523536028747135266249775724709369995957; 31 | const float pi = 3.141592653589793238462643383279502884197169; 32 | const float n = 1.0003; // refractive index of air 33 | const float N = 2.545E25; // number of molecules per unit volume for air at 34 | // 288.15K and 1013mb (sea level -45 celsius) 35 | const float pn = 0.035; // depolatization factor for standard air 36 | // wavelength of used primaries, according to preetham 37 | const vec3 lambda = vec3(680E-9, 550E-9, 450E-9); 38 | // mie stuff 39 | // K coefficient for the primaries 40 | const vec3 K = vec3(0.686, 0.678, 0.666); 41 | const float v = 4.0; 42 | // optical length at zenith for molecules 43 | const float rayleighZenithLength = 8.4E3; 44 | const float mieZenithLength = 1.25E3; 45 | const vec3 up = vec3(0.0, 1.0, 0.0); 46 | const float EE = 1000.0; 47 | const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324; 48 | // 66 arc seconds -> degrees, and the cosine of that 49 | // earth shadow hack 50 | const float cutoffAngle = pi/1.95; 51 | const float steepness = 1.5; 52 | vec3 totalRayleigh(vec3 lambda) 53 | { 54 | return (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)); 55 | } 56 | // A simplied version of the total Reayleigh scattering to works on browsers that use ANGLE 57 | vec3 simplifiedRayleigh() 58 | { 59 | return 0.0005 / vec3(94, 40, 18); 60 | } 61 | float rayleighPhase(float cosTheta) 62 | { 63 | return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0)); 64 | // return (1.0 / (3.0*pi)) * (1.0 + pow(cosTheta, 2.0)); 65 | // return (3.0 / 4.0) * (1.0 + pow(cosTheta, 2.0)); 66 | } 67 | vec3 totalMie(vec3 lambda, vec3 K, float T) 68 | { 69 | float c = (0.2 * T ) * 10E-18; 70 | return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K; 71 | } 72 | float hgPhase(float cosTheta, float g) 73 | { 74 | return (1.0 / (4.0*pi)) * ((1.0 - pow(g, 2.0)) / pow(1.0 - 2.0*g*cosTheta + pow(g, 2.0), 1.5)); 75 | } 76 | float sunIntensity(float zenithAngleCos) 77 | { 78 | return EE * max(0.0, 1.0 - exp(-((cutoffAngle - acos(zenithAngleCos))/steepness))); 79 | } 80 | // float logLuminance(vec3 c) 81 | // { 82 | // return log(c.r * 0.2126 + c.g * 0.7152 + c.b * 0.0722); 83 | // } 84 | // Filmic ToneMapping http://filmicgames.com/archives/75 85 | float A = 0.15; 86 | float B = 0.50; 87 | float C = 0.10; 88 | float D = 0.20; 89 | float E = 0.02; 90 | float F = 0.30; 91 | float W = 1000.0; 92 | vec3 Uncharted2Tonemap(vec3 x) 93 | { 94 | return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; 95 | } 96 | void main() 97 | { 98 | float sunfade = 1.0-clamp(1.0-exp((sunPosition.y/450000.0)),0.0,1.0); 99 | // luminance = 1.0 ;// vWorldPosition.y / 450000. + 0.5; //sunPosition.y / 450000. * 1. + 0.5; 100 | // gl_FragColor = vec4(sunfade, sunfade, sunfade, 1.0); 101 | float reileighCoefficient = reileigh - (1.0* (1.0-sunfade)); 102 | vec3 sunDirection = normalize(sunPosition); 103 | float sunE = sunIntensity(dot(sunDirection, up)); 104 | // extinction (absorbtion + out scattering) 105 | // rayleigh coefficients 106 | vec3 betaR = simplifiedRayleigh() * reileighCoefficient; 107 | // mie coefficients 108 | vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient; 109 | // optical length 110 | // cutoff angle at 90 to avoid singularity in next formula. 111 | float zenithAngle = acos(max(0.0, dot(up, normalize(vWorldPosition - cameraPos)))); 112 | float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253)); 113 | float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253)); 114 | // combined extinction factor 115 | vec3 Fex = exp(-(betaR * sR + betaM * sM)); 116 | // in scattering 117 | float cosTheta = dot(normalize(vWorldPosition - cameraPos), sunDirection); 118 | float rPhase = rayleighPhase(cosTheta*0.5+0.5); 119 | vec3 betaRTheta = betaR * rPhase; 120 | float mPhase = hgPhase(cosTheta, mieDirectionalG); 121 | vec3 betaMTheta = betaM * mPhase; 122 | vec3 Lin = pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex),vec3(1.5)); 123 | Lin *= mix(vec3(1.0),pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * Fex,vec3(1.0/2.0)),clamp(pow(1.0-dot(up, sunDirection),5.0),0.0,1.0)); 124 | //nightsky 125 | vec3 direction = normalize(vWorldPosition - cameraPos); 126 | float theta = acos(direction.y); // elevation --> y-axis, [-pi/2, pi/2] 127 | float phi = atan(direction.z, direction.x); // azimuth --> x-axis [-pi/2, pi/2] 128 | vec2 uv = vec2(phi, theta) / vec2(2.0*pi, pi) + vec2(0.5, 0.0); 129 | // vec3 L0 = texture2D(skySampler, uv).rgb+0.1 * Fex; 130 | vec3 L0 = vec3(0.1) * Fex; 131 | // composition + solar disc 132 | //if (cosTheta > sunAngularDiameterCos) 133 | float sundisk = smoothstep(sunAngularDiameterCos,sunAngularDiameterCos+0.00002,cosTheta); 134 | // if (normalize(vWorldPosition - cameraPos).y>0.0) 135 | L0 += (sunE * 19000.0 * Fex)*sundisk; 136 | vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W)); 137 | vec3 texColor = (Lin+L0); 138 | texColor *= 0.04 ; 139 | texColor += vec3(0.0,0.001,0.0025)*0.3; 140 | float g_fMaxLuminance = 1.0; 141 | float fLumScaled = 0.1 / luminance; 142 | float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1.0 + fLumScaled); 143 | float ExposureBias = fLumCompressed; 144 | vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor); 145 | vec3 color = curr*whiteScale; 146 | vec3 retColor = pow(color,vec3(1.0/(1.2+(1.2*sunfade)))); 147 | gl_FragColor.rgb = retColor; 148 | gl_FragColor.a = 1.0; 149 | } 150 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GLSL Token Viewer 5 | 6 | 7 | 125 | Editor: (paste or write code here) 126 | 140 | 141 | 142 | 143 | 144 | 145 |

146 | 
147 | 	
148 | Token Viewer: (click on code to view tokens) 149 |

150 | 
151 | 
152 | 
153 | 
154 | 
155 | 


--------------------------------------------------------------------------------
/lint/lint.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | function lint(ast, passes) {
 4 | 	var reporter = new Reporter();
 5 | 	passes.forEach(function(pass) {
 6 | 		pass(ast, reporter);
 7 | 	});
 8 | 	return reporter.reports;
 9 | }
10 | 
11 | function lint_all(ast) {
12 | 	return lint(ast, [unused_variables]);
13 | }
14 | 
15 | function Report(message, details) {
16 | 	this.message = message;
17 | 	this.details = details;
18 | }
19 | 
20 | function Reporter() {
21 | 	this.reports = [];
22 | }
23 | 
24 | Reporter.prototype.report = function(message, details) {
25 | 	var msg = 'Warning on ' + details.token.line + ':' + details.token.column + ' - ' + message;
26 | 	console.log(msg);
27 | 	this.reports.push(new Report(message, details));
28 | };


--------------------------------------------------------------------------------
/lint/unused.js:
--------------------------------------------------------------------------------
 1 | /*
 2 | 
 3 | Exports: unused_variables
 4 | 
 5 | 
 6 | float a;
 7 | float b = 1.;
 8 | float c, d;
 9 | float e = 2., f = 3.;
10 | 
11 | void test(float g, float h) {
12 |   float i;
13 | }
14 | */
15 | 
16 | var exceptions = ['main'];
17 | 
18 | function unused_variables(ast, reporter, global_declared) {
19 | 	// exception - varying depends on in vs / fs.
20 | 	var declared_list = {}; // symbol table
21 | 	var warnings = [];
22 | 
23 | 	simple_walker(ast, function(node) {
24 | 		if (node.type === 'decllist') {
25 | 			walk_into(node, 'ident', function(node) {
26 | 				declared_list[node.data] = {
27 | 					type: 'variable',
28 | 					name: node.data,
29 | 					token: node.token,
30 | 					calls: 0
31 | 				};
32 | 			});
33 | 
34 | 			return;
35 | 		}
36 | 
37 | 		if (node.type === 'function') {
38 | 			var functionNode = node.children[0];
39 | 
40 | 			declared_list[functionNode.data] = {
41 | 				type: 'function', // technically a function variable
42 | 				name: functionNode.data,
43 | 				token: functionNode.token,
44 | 				calls: 0
45 | 			};
46 | 
47 | 			return;
48 | 		}
49 | 
50 | 		if (node.type === 'expr') {
51 | 			walk_into(node, 'ident', function(node) {
52 | 				if (node.data in declared_list) {
53 | 					declared_list[node.data].calls++;
54 | 				}
55 | 				else if (global_declared && node.data in global_declared) {
56 | 					global_declared[node.data].calls++;
57 | 				}
58 | 				else {
59 | 					// TODO: add exceptions?
60 | 
61 | 					// warn that variable has not been declared!
62 | 					if (exceptions.indexOf(node.data) === -1)
63 | 						console.warn('Variable not found!', node.data);
64 | 
65 | 
66 | 				}
67 | 			});
68 | 
69 | 			return;
70 | 		}
71 | 
72 | 		if (node.type === 'function' && !global_declared) {
73 | 			console.log('function!');
74 | 
75 | 			unused_variables(node, reporter, declared_list);
76 | 
77 | 			return true; // break walker
78 | 		}
79 | 	});
80 | 
81 | 	console.log('declared variables', declared_list, Object.keys(declared_list));
82 | 
83 | 	for (var variable in declared_list) {
84 | 		var symbol = declared_list[variable];
85 | 		if (!symbol.calls && exceptions.indexOf(variable) === -1) {
86 | 			var msg = symbol.type + ' [' + symbol.name + '] is not used';
87 | 			reporter.report(msg, symbol);
88 | 		}
89 | 	}
90 | 
91 | 	return;
92 | 	// decl < decllist
93 | 	// stmt / func / functionargs
94 | }


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "glsl-cleaner",
 3 |   "version": "1.0.0",
 4 |   "description": "GLSL Shaker / Clean / Pruner / Code Code Eliminator",
 5 |   "main": "glslprep.js",
 6 |   "scripts": {
 7 |     "b": "browserify view.js -o b.js",
 8 |     "w": "watchify view.js -o b.js",
 9 |     "test": "echo \"Error: no test specified\" && exit 1"
10 | 
11 |   },
12 |   "author": "Joshua Koo (zz85nus@gmail.com)",
13 |   "license": "MIT",
14 |   "dependencies": {
15 |     "glsl-parser": "file:glsl-parser"
16 |   }
17 | }
18 | 


--------------------------------------------------------------------------------
/run.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | 
  3 | function ajax(url, callback) {
  4 | 	var oReq = new XMLHttpRequest();
  5 | 	oReq.open('GET', url, true);
  6 | 
  7 | 	oReq.onload = function(oEvent) {
  8 | 		console.log('ajax done for ', url);
  9 | 		var response = oReq.response;
 10 | 		callback(response);
 11 | 	};
 12 | 
 13 | 	oReq.send();
 14 | }
 15 | 
 16 | var SHOW_LINE_BREAKS = true;
 17 | 
 18 | source.onchange = function() {
 19 | 	attemptParse();
 20 | };
 21 | 
 22 | source.onkeyup = function() {
 23 | 	attemptParse();
 24 | };
 25 | 
 26 | // shader_submerged.glsl vs.glsl
 27 | ajax('shader_submerged.glsl', function(r) {
 28 | 	// console.log('got', r);
 29 | 	source.value = r;
 30 | 	startParsing(r)
 31 | });
 32 | 
 33 | setTimeout(attemptParse, 100);
 34 | 
 35 | var glsl_container;
 36 | var token_to_dom = [];
 37 | var highlighted, tokens, ast;
 38 | 
 39 | function startParsing(code) {
 40 | 	try {
 41 | 		tokens = tokenize(code);
 42 | 		// console.log(code.length, tokens.length);
 43 | 
 44 | 		if (glsl_container) glsl_container.parentNode.removeChild(glsl_container)
 45 | 		glsl_container = document.createElement('pre');
 46 | 		glsl_container.id = 'glsl_container';
 47 | 
 48 | 		// glsl_container.contentEditable = true;
 49 | 
 50 | 		// glsl_container.onkeyup = function() {
 51 | 
 52 | 		// }
 53 | 
 54 | 		document.body.appendChild(glsl_container);
 55 | 
 56 | 		tokenHighlighter(tokens);
 57 | 
 58 | 		ast = parse(tokens);
 59 | 	} catch (e) {
 60 | 		debug.innerHTML = 'Error Parsing GLSL Code! ' + e;
 61 | 		return e;
 62 | 	}
 63 | 
 64 | 	debug.innerHTML = '';
 65 | 
 66 | 	tree.innerHTML = 'Ast:\n';
 67 | 	walker(ast);
 68 | 
 69 | 
 70 | 	/*
 71 | 	// check children types
 72 | 	var TYPE_CHILDREN = {};
 73 | 	simple_walker(ast, function(node) {
 74 | 		TYPE_CHILDREN[node.type] = TYPE_CHILDREN[node.type] || {};
 75 | 		node.children.forEach(function(child) {
 76 | 			TYPE_CHILDREN[node.type][child.type] = 1;
 77 | 		})
 78 | 	});
 79 | 	for (k in TYPE_CHILDREN) console.log(k, Object.keys(TYPE_CHILDREN[k]))
 80 | 	*/
 81 | 
 82 | 	console.time('lint');
 83 | 	var reports = lint_all(ast);
 84 | 
 85 | 	reports.forEach(function(r) {
 86 | 		var a = document.createElement('a');
 87 | 		a.className = 'errorlink';
 88 | 		a.onclick = function() {
 89 | 			// console.log(r.details.token);
 90 | 			hightlightToken(r.details.token, true);
 91 | 			return false;
 92 | 		}
 93 | 		a.innerHTML = 'Warning on ' + r.details.token.line + ':' + r.details.token.column + ' - ' + r.message + '\n';
 94 | 
 95 | 		debug.appendChild(a);
 96 | 	})
 97 | 	console.timeEnd('lint');
 98 | }
 99 | 
100 | function tokenHighlighter(tokens) {
101 | 	tokens.forEach(eatToken)
102 | }
103 | 
104 | function eatToken(token) {
105 | 	var span = document.createElement('span');
106 | 	span.className = 'token ' + token.type;
107 | 	span.textContent = token.data;
108 | 	glsl_container.appendChild(span);
109 | 
110 | 	if (SHOW_LINE_BREAKS && token.data.match(/\n+/g)) {
111 | 		span.innerHTML = token.data.replace(/\n/g, '↵\n');
112 | 	}
113 | 
114 | 	span.onclick = function() {
115 | 		console.log(token);
116 | 		hightlightToken(token);
117 | 	}
118 | 
119 | 	token_to_dom.push(span);
120 | }
121 | 
122 | var level = 0;
123 | 
124 | function hightlightToken(token, hide) {
125 | 	var token_index = tokens.indexOf(token);
126 | 	if (token_index > -1) {
127 | 		if (highlighted) highlighted.classList.remove('highlight');
128 | 		highlighted = token_to_dom[token_index];
129 | 		highlighted.classList.add('highlight');
130 | 
131 | 		highlighted.scrollIntoView();
132 | 
133 | 		// highlighted.parentNode.scrollTop = (ast.token.line * 12) + 'px';
134 | 		// highlighted.scrollIntoView({behavior: "smooth"}); // block: "end",
135 | 	} else {
136 | 		console.log('token not found');
137 | 	}
138 | 
139 | 	if (!hide) debug.innerHTML = JSON.stringify(token);
140 | }
141 | 
142 | 
143 | function walker(ast) {
144 | 	level++;
145 | 
146 | 	if (ast.type !== 'placeholder') {
147 | 		tree.appendChild(document.createTextNode(new Array(level).join('|') + '- '));
148 | 
149 | 
150 | 		var a = document.createElement('a');
151 | 		a.innerHTML = ast.type;
152 | 		a.href = '#';
153 | 		a.onclick = function() {
154 | 			hightlightToken(ast.token);
155 | 			console.log('ast', ast);
156 | 			return false;
157 | 		}
158 | 
159 | 		tree.appendChild(a);
160 | 
161 | 		tree.appendChild(document.createTextNode(' ' + (ast.token ? ast.token.data : '') + '\n'));
162 | 	}
163 | 
164 | 	ast.children.forEach(walker);
165 | 	level--;
166 | 
167 | 	// mknode(mode/type, token, children, id)
168 | }
169 | 
170 | 
171 | var isParsing, lastParsed;
172 | 
173 | function attemptParse() {
174 | 	if (isParsing) {
175 | 		clearTimeout(isParsing);
176 | 	}
177 | 	isParsing = setTimeout(tryParse, 200);
178 | }
179 | 
180 | 
181 | function tryParse() {
182 | 	clearTimeout(isParsing);
183 | 
184 | 	var contents = source.value;
185 | 	// var contents = glsl_container.textContent.split('↵').join('');
186 | 	// no thanks to no unicode reg expr
187 | 	if (contents == lastParsed) return;
188 | 
189 | 	startParsing(contents);
190 | 	lastParsed = contents;
191 | }
192 | 


--------------------------------------------------------------------------------
/shader_submerged.glsl:
--------------------------------------------------------------------------------
  1 | // From https://www.shadertoy.com/view/Xsf3RB
  2 | 
  3 | #define MODEL_JELLYFISH  0
  4 | #define MODEL_FISH       1
  5 | #define MODEL_GROUND     2
  6 | 
  7 | vec2 t;
  8 | float time;
  9 | float anim_offset = 0.0;
 10 | int model = 0;
 11 | mat3 rot, trot, rx;
 12 | float znear;
 13 | vec3 glow;
 14 | vec2 p;
 15 | float tm2;
 16 | vec2 ltc;
 17 | int material;
 18 | 
 19 | mat3 rotateYmat(float ang)
 20 | {
 21 | 	return mat3(cos(ang), 0.0, sin(ang),
 22 | 				0.0, 1.0, 0.0,
 23 | 				-sin(ang), 0.0, cos(ang));
 24 | }
 25 | 
 26 | mat3 rotateXmat(float ang)
 27 | {
 28 | 	return mat3(0.0, 0.0, 1.0,
 29 | 				cos(ang), sin(ang), 0.0,
 30 | 				-sin(ang), cos(ang), 0.0);
 31 | }
 32 | 
 33 | float traceSphere(vec3 ro, vec3 rd, float radsq)
 34 | {
 35 | 	float a = dot(rd, rd);
 36 | 	float b = dot(ro, rd);
 37 | 	float c = dot(ro, ro) - radsq;
 38 | 	float d = (b * b - a * c) * 4.0;
 39 | 
 40 | 	return mix(1e4, (-b + sqrt(d)) / (2.0 * a), step(0.0, d));
 41 | }
 42 | 
 43 | 
 44 | vec3 tex(vec2 p)
 45 | {
 46 | 	float su = 0.5 + 0.5 * cos(p.x * 4.0);
 47 | 	vec3 c = mix(vec3(0.5, 0.1, 0.1) * 0.3, vec3(1.0, 0.9, 0.5), 1.0 - p.y + cos(time * 8.0) * 0.02);
 48 | 	vec3 c2 = c * mix(vec3(1.0, 0.9, 0.5), vec3(0.7, 0.5, 0.1), su);
 49 | 	return mix(c2, c, sqrt(1.0 - p.y)) + pow(su * (0.5 + 0.5 * cos(p.y * 40.0)) * 1.02, 64.0) * vec3(0.1);
 50 | }
 51 | 
 52 | float radius(float x)
 53 | {
 54 | 	if(model == MODEL_JELLYFISH)
 55 | 	{
 56 | 		float t = time + anim_offset;
 57 | 		return (x * 0.3 + (0.5 + 0.5 * cos(x * 4.0 + t * 10.0)) * 0.04);
 58 | 	}
 59 | 
 60 | 	if(model == MODEL_GROUND)
 61 | 	{
 62 | 		return x * 0.5;
 63 | 	}
 64 | 
 65 | 	return mix(x * 0.03, 1.0, 0.5 + 0.5 * sin(x * 0.8 - 0.6)) * 0.6;
 66 | }
 67 | 
 68 | float zpos(float x)
 69 | {
 70 | 	if(model == MODEL_JELLYFISH)
 71 | 	{
 72 | 		float t = time + anim_offset;
 73 | 		return cos(x * 0.5 + t) * 2.0 * (1.0 + iMouse.x / iResolution.x * 2.0);
 74 | 	}
 75 | 
 76 | 	if(model == MODEL_GROUND)
 77 | 	{
 78 | 		return cos(x / 8.0 * 3.14159 * 2.0 + 0.1);
 79 | 	}
 80 | 
 81 | 	return x * 0.4 + cos(x + time * 4.0) * 0.1;
 82 | }
 83 | 
 84 | void swap(inout float a, inout float b)
 85 | {
 86 | 	float c = a;
 87 | 	a = b;
 88 | 	b = c;
 89 | }
 90 | 
 91 | void tubePiece(int j, vec3 ro, vec3 rd, inout vec2 tc, inout float tm)
 92 | {
 93 |     float s0 = radius(float(j)), s1 = radius(float(j + 1));
 94 |     float z0 = zpos(float(j)), z1 = zpos(float(j + 1));
 95 | 
 96 |     float zz0 = -z1;
 97 |     float f = z1 - z0;
 98 | 
 99 |     if(z0 < z1)
100 |     {
101 |         swap(s0, s1);
102 |         swap(z0, z1);
103 |     }
104 | 
105 |     float i = 0.0;
106 |     vec3 oro = ro, ord = rd;
107 | 
108 | 
109 | 	float zd = z0 - z1;
110 | 
111 | 	float u = (s1 - s0) / zd;
112 | 
113 | 	float zofs = s0 / u;
114 | 	float rus = 1.0 / (u * u);
115 | 
116 | 	ro.z += zofs + z0;
117 | 
118 | 	float a = dot(rd.xy, rd.xy) * rus - rd.z * rd.z;
119 | 	float b = dot(ro.xy, rd.xy) * rus - rd.z * ro.z;
120 | 	float c = dot(ro.xy, ro.xy) * rus - ro.z * ro.z;
121 | 	float d = b * b - a * c;
122 | 
123 | 	if(d < 0.0)
124 | 		return;
125 | 
126 | 	d = sqrt(d);
127 | 
128 | 	float cone_min = (-b - d) / a;
129 | 	float cone_max = (-b + d) / a;
130 | 
131 | 	float rp0 = ro.z + rd.z * cone_min - zofs;
132 | 	float rp1 = ro.z + rd.z * cone_max - zofs;
133 | 
134 | 
135 | 	if(rp0 > 0.0 && rp0 < zd && cone_min > 0.0)
136 | 		i = cone_min;
137 | 	else if(rp1 > 0.0 && rp1 < zd && cone_max > 0.0)
138 | 		i = cone_max;
139 | 	else
140 | 		return;
141 | 
142 | 
143 |     ro = oro;
144 |     rd = ord;
145 | 
146 | 	vec3 rp = ro + rd * i;
147 | 	float t = (rp.z - zz0) / f;
148 | 
149 | 	vec2 ltc = vec2(atan(rp.y, rp.x) * 3.0, mix(float(j + 1), float(j), t));
150 | 	float hit = step(i, tm);
151 | 
152 | 	tc = mix(tc, ltc, hit);
153 | 	tm = mix(tm, i, hit);
154 | }
155 | 
156 | 
157 | float tube(vec3 ro, vec3 rd, inout vec2 tc)
158 | {
159 | 	float tm = 1e2;
160 | 
161 |     tubePiece(0, ro, rd, tc, tm);
162 |     tubePiece(1, ro, rd, tc, tm);
163 |     tubePiece(2, ro, rd, tc, tm);
164 |     tubePiece(3, ro, rd, tc, tm);
165 |     tubePiece(4, ro, rd, tc, tm);
166 |     tubePiece(5, ro, rd, tc, tm);
167 |     tubePiece(6, ro, rd, tc, tm);
168 |     tubePiece(7, ro, rd, tc, tm);
169 | 
170 | 	tc.y /= float(8);
171 | 
172 | 	return tm;
173 | }
174 | 
175 | vec3 backgtex(vec3 p)
176 | {
177 | 	float f = clamp(smoothstep(8.0, 30.0, p.y), 0.0, 1.0);
178 | 	vec3 n = pow(smoothstep(10.0, 42.0, p.y), 100.0) * vec3(1.0, 0.9, 0.5);
179 | 	return (mix(vec3(0.1, 0.2, 0.2), vec3(0.0, 0.1, 0.1), 0.5 + 0.5 * cos(p.y * 0.4 + time * 10.0)) * 0.4 * f + n) * 0.5
180 | 		+ vec3(0.05, 0.06, 0.1) * smoothstep(-40.0, 1.0, p.y);
181 | }
182 | 
183 | float noise( vec2 x )
184 | {
185 | 	return texture2D(iChannel0, x / 64.0).r;
186 | }
187 | 
188 | mat3 transpose(mat3 m)
189 | {
190 | 	return mat3(m[0].x, m[1].x, m[2].x,
191 | 				m[0].y, m[1].y, m[2].y,
192 | 				m[0].z, m[1].z, m[2].z);
193 | }
194 | 
195 | 
196 | void traceFish(int i, inout vec2 tc, inout float tm, vec3 ro, vec3 rd)
197 | {
198 | 	mat3 r = rotateXmat(time + float(i) * 0.4 + cos(time + float(i))), trans_r = transpose(r);
199 | 
200 | 	vec3 tr = rotateYmat(float(i)) * rotateXmat(float(i)) * vec3(0.0, float(i), float(i)) * 4.0 +
201 | 		r * vec3(0.0, 8.0, 0.0);
202 | 
203 | 	vec3 vtr = trot * (tr - ro);
204 | 
205 | 	if(vtr.z < -1e-3)
206 | 	{
207 | 		vec2 ptr = vtr.xy / vtr.z * znear;
208 | 		glow += vec3(0.3, 0.7, 1.0).bgr * pow(max(0.0, 1.0 - distance(ptr, p) * 1.0) * 5.0, 2.0) * 0.4;
209 | 	}
210 | 
211 | 	anim_offset = float(i) * 1.7;
212 | 
213 | 	vec3 s = vec3(1.0, 2.0, 1.0);
214 | 
215 | 	tm2 = tube((trans_r * (ro - tr)) * s, (trans_r * rd) * s, ltc);
216 | 
217 | 	tc = mix(tc, ltc, step(tm2, tm));
218 | 	tm = min(tm, tm2);
219 | }
220 | 
221 | void traceJellyFish(int i, inout vec2 tc, inout float tm, vec3 ro, vec3 rd)
222 | {
223 | 	mat3 r = rotateYmat(float(i) * 2.0) * rx, trans_r = transpose(r);
224 | 	vec3 tr = rotateYmat(float(i)) * vec3(float(i) * 1.0, sin(float(i) * 5.0) * 10.0, 4.0) *
225 | 		(1.0 + iMouse.y / iResolution.y * 2.0);
226 | 
227 | 	vec3 vtr = trot * (tr - ro);
228 | 
229 | 	if(vtr.z < -1e-3)
230 | 	{
231 | 		vec2 ptr = vtr.xy / vtr.z * znear;
232 | 		glow += vec3(0.3, 0.7, 1.0) * pow(max(0.0, 1.0 - distance(ptr, p) * 2.0) * 7.0, 2.0) *
233 | 			mix(0.7, 1.2, 0.5 + 0.5 * cos(float(i) + time * 4.0));
234 | 	}
235 | 
236 | 	anim_offset = float(i) * 1.7;
237 | 
238 | 	tm2 = tube(trans_r * (ro - tr), trans_r * rd, ltc);
239 | 
240 | 	if(tm2 < tm)
241 | 		material = MODEL_JELLYFISH;
242 | 
243 | 	tc = mix(tc, ltc, step(tm2, tm));
244 | 	tm = min(tm, tm2);
245 | }
246 | 
247 | void mainImage( out vec4 fragColor, in vec2 fragCoord )
248 | {
249 | 	vec2 uv = fragCoord.xy / iResolution.xy;
250 | 	t = uv * 2.0 - vec2(1.0);
251 | 	t.x *= iResolution.x / iResolution.y;
252 | 
253 | 	time = iGlobalTime;
254 | 
255 | 	material = MODEL_FISH;
256 | 	znear = -0.8;
257 | 	rot = rotateXmat(3.1415926 * 0.5 + cos(time * 0.1) + sin(time * 5.0) * 0.002) * rotateYmat(time * 0.2);
258 | 	trot = transpose(rot);
259 | 
260 | 	p = t.xy;
261 | 
262 | 	vec3 ro = rot * vec3(cos(time), sin(time * 0.3), 10.0);
263 | 	vec3 rd = rot * vec3(p, znear);
264 | 	float tm = 2e2;
265 | 
266 | 	vec2 tc = vec2(0.0);
267 | 
268 | 	glow = vec3(0.0);
269 | 
270 | 	model = MODEL_FISH;
271 | 	for(int i = 0; i < 8; i += 1)
272 | 		traceFish(i, tc, tm, ro, rd);
273 | 
274 | 
275 | 
276 | 
277 |     model = MODEL_JELLYFISH;
278 | 	rx = rotateXmat(3.1415926);
279 | 
280 | 	for(int i = 0; i < 10; i += 1)
281 | 		traceJellyFish(i, tc, tm, ro, rd);
282 | 
283 | 
284 | 
285 | 
286 | 	// ground
287 | 
288 | 	model = MODEL_GROUND;
289 | 	mat3 r = rotateXmat(-3.1415926), trans_r = transpose(r);
290 | 	vec3 tr = vec3(0.0, -24.0, 0.0);
291 | 
292 | 	vec3 vtr = trot * (tr - ro);
293 | 
294 | 	anim_offset = 1.7;
295 | 
296 | 	vec3 s = vec3(0.2, 0.2, 0.2);
297 | 
298 | 	tm2 = tube(trans_r * (ro - tr) * s, trans_r * rd * s, ltc);
299 | 
300 | 	if(tm2 < tm)
301 | 		material = MODEL_GROUND;
302 | 
303 | 	tc = mix(tc, ltc, step(tm2, tm));
304 | 	tm = min(tm, tm2);
305 | 
306 | 
307 | 
308 | 
309 | 	vec3 rp = ro + rd * tm;
310 | 
311 | 
312 | 	tm2 = traceSphere(ro, rd, 40.0 * 40.0);
313 | 	vec3 backgcol = backgtex(ro + rd * tm2);
314 | 
315 | 	vec3 c;
316 | 
317 | 	if(material == MODEL_FISH)
318 | 		c = tex(tc);
319 | 
320 | 	if(material == MODEL_JELLYFISH)
321 | 		c = tex(tc).bgr;
322 | 
323 | 	if(material == MODEL_GROUND)
324 | 		c = mix(0.3 * mix(vec3(0.5, 0.3, 0.3), vec3(1.0, 1.0, 0.4), mix(0.3, 0.5, noise(rp.xz * 8.0))),
325 | 				backgcol, pow(smoothstep(0.0, 1.0, tc.y * 1.0), 2.0)) * smoothstep(-10.0, 1.0, rp.y - -25.0);
326 | 
327 | 	float v = pow(1.0 - smoothstep(0.0, 2.0, length(p)), 0.5);
328 | 
329 | 	vec3 col = (mix(vec3(0.0), c, step(tm, 0.9e2))) * 1.1;
330 | 
331 | 	float bs = clamp(1.0 - length(backgcol) * 4.0, 0.0, 1.0) * v * 3.5;
332 | 
333 | 	fragColor.rgb = (mix(backgcol, col, step(tm, tm2)) + glow * 0.01) * v;
334 | 	fragColor.rgb += noise(fragCoord.xy) * 0.01;
335 | 
336 | 	fragColor.rgb += smoothstep(0.9, 0.91,
337 | 								   noise(t.xy * 40.0 + vec2(cos(time + t.y * 5.0) * 4.0, time * 10.0 + cos(time) * 5.0 + sin(time + t.x * 3.0) * 4.0) )) * 0.01 *
338 | 		smoothstep(0.0, 20.0, tm) * bs;
339 | 
340 | 	fragColor.rgb += smoothstep(0.9, 0.91,
341 | 								   noise(t.xy * 80.0 + vec2(cos(time + t.y * 10.0) * 4.0, time * 10.0 + cos(time + 0.1) * 5.0 + sin(time * 0.5 + t.x * 8.0) * 4.0) )) * 0.01 *
342 | 		smoothstep(0.0, 15.0, tm) * 2.0 * bs;
343 | 
344 | 	fragColor.rgb = pow(fragColor.rgb, vec3(0.8));
345 | }
346 | 


--------------------------------------------------------------------------------
/view.js:
--------------------------------------------------------------------------------
 1 | var TokenString = require('./glsl-tokenizer/string')
 2 | var ParseTokens = require('./glsl-parser/direct')
 3 | 
 4 | window.process = process;
 5 | window.tokenize = TokenString;
 6 | window.parse = ParseTokens;
 7 | 
 8 | function process(code) {
 9 | 	tokens = TokenString(code)
10 | 	ast = ParseTokens(tokens)
11 | 	console.log(JSON.stringify(tokens))
12 | }


--------------------------------------------------------------------------------
/vs.glsl:
--------------------------------------------------------------------------------
 1 | // This is a comment
 2 | 
 3 | /*
 4 |  This is a block comment
 5 | 
 6 | */
 7 | 
 8 | precision highp float;
 9 | precision highp int;
10 | #define SHADER_NAME ShaderMaterial
11 | #define VERTEX_TEXTURES
12 | #define GAMMA_FACTOR 2
13 | #define MAX_DIR_LIGHTS 0
14 | #define MAX_POINT_LIGHTS 0
15 | #define MAX_SPOT_LIGHTS 0
16 | #define MAX_HEMI_LIGHTS 0
17 | #define MAX_SHADOWS 0
18 | #define MAX_BONES 251
19 | #define FLIP_SIDED
20 | uniform mat4 modelMatrix;
21 | uniform mat4 modelViewMatrix;
22 | uniform mat4 projectionMatrix;
23 | uniform mat4 viewMatrix;
24 | uniform mat3 normalMatrix;
25 | uniform vec3 cameraPosition;
26 | attribute vec3 position;
27 | attribute vec3 normal;
28 | attribute vec2 uv;
29 | #ifdef USE_COLOR
30 | 	attribute vec3 color;
31 | #endif
32 | 
33 | 
34 | 
35 | #ifdef USE_MORPHTARGETS
36 | 	attribute vec3 morphTarget0;
37 | 	attribute vec3 morphTarget1;
38 | 	attribute vec3 morphTarget2;
39 | 	attribute vec3 morphTarget3;
40 | 	#ifdef USE_MORPHNORMALS
41 | 		attribute vec3 morphNormal0;
42 | 		attribute vec3 morphNormal1;
43 | 		attribute vec3 morphNormal2;
44 | 		attribute vec3 morphNormal3;
45 | 	#else
46 | 		attribute vec3 morphTarget4;
47 | 		attribute vec3 morphTarget5;
48 | 		attribute vec3 morphTarget6;
49 | 		attribute vec3 morphTarget7;
50 | 	#endif
51 | #endif
52 | #ifdef USE_SKINNING
53 | 	attribute vec4 skinIndex;
54 | 	attribute vec4 skinWeight;
55 | #endif
56 | 
57 | varying vec3 vWorldPosition;
58 | void main() {
59 | vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
60 | vWorldPosition = worldPosition.xyz;
61 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
62 | }
63 | 
64 | 


--------------------------------------------------------------------------------
/walk.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | // walker which executes a callback on every node.
 4 | function simple_walker(ast, onNode) {
 5 | 	if (onNode(ast)) return;
 6 | 
 7 | 	ast.children.forEach(function(child) {
 8 | 		simple_walker(child, onNode);
 9 | 	});
10 | }
11 | 
12 | function walk_into(ast, type, onNode) {
13 | 	simple_walker(ast, function(node) {
14 | 		if (node.type === type) { // stmtlist
15 | 			onNode(node);
16 | 		}
17 | 	});
18 | }


--------------------------------------------------------------------------------