Last active
August 29, 2015 14:25
-
-
Save samsartor/ce93ea27fda816bdca91 to your computer and use it in GitHub Desktop.
Help making the gl.json config for bluegin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// This is a node.js script designed to create the base JSON cfg file for the auto-gen of the GL, GLEnum, and GLCapabilities classes in BlueGin | |
// It looks at the C headers in the opengl registry (https://www.opengl.org/registry/) and gives us a starting point | |
// | |
var http = require('http'); | |
var https = require('https'); | |
var fs = require('fs'); | |
var StringDecoder = require('string_decoder').StringDecoder; | |
// Pipes a stream into a string and passes that string to a callback | |
function pipeToString(readable, callback) { | |
var decoder = new StringDecoder('utf8'); | |
var out = ''; | |
readable.on('readable', function(){ | |
var chunk = readable.read(); | |
if (chunk != null) | |
{ | |
out += decoder.write(chunk); | |
} | |
}); | |
readable.on('end', function() { | |
callback(out); | |
}); | |
} | |
//downloads data from a url (http or https) and passes it it the callback as a string | |
function downloadString(url, callback) { | |
var getter; | |
if (url.indexOf('https') == 0) | |
{ | |
getter = https; | |
} | |
else | |
{ | |
getter = http; | |
} | |
getter.get(url, function(resp) { | |
if (resp.statusCode != 200) | |
{ | |
console.log("Could not download \"%s\": Code %d", url, resp.statusCode); | |
} | |
else | |
{ | |
pipeToString(resp, callback); | |
} | |
}); | |
} | |
//capitalize the first letter in the string | |
String.prototype.capitalize = function() { | |
return this.charAt(0).toUpperCase() + this.slice(1); | |
} | |
//un-capitalize the first letter in the string | |
String.prototype.decapitalize = function() { | |
return this.charAt(0).toLowerCase() + this.slice(1); | |
} | |
//initial output | |
var cfg = {'_comment': [], groups: [], enums: {}, funcs: {}}; | |
//These are the lines in the header file, for some reason | |
var glext = []; | |
//Any core GL group should match this | |
var glcoregroup = /GL_VERSION_\d+_\d+/; | |
//Download the core | |
downloadString("https://www.opengl.org/registry/api/GL/glcorearb.h", function (out){ | |
//Do the nasty parsing | |
parse(out, false); | |
//Download the extentions | |
downloadString("https://www.opengl.org/registry/api/GL/glext.h", function (out){ | |
parse(out, true); | |
//We want to only use some of the stuff, so we are going to create a new cfg object and move stuff into it | |
var fcfg = {'_comment': cfg['_comment'], groups: cfg.groups, enums: {}, funcs: {}}; | |
for (i in cfg.enums) | |
{ | |
//the enum should not be marked as depricated | |
if (typeof cfg.enums[i].dep === 'undefined' || cfg.enums[i].dep == false) | |
{ | |
//check all sources of the enum and only include it if it appears in the core | |
for (j in cfg.enums[i].from) | |
{ | |
if (j.match(glcoregroup)) | |
{ | |
fcfg.enums[i] = cfg.enums[i]; | |
break; | |
} | |
} | |
} | |
} | |
for (i in cfg.funcs) | |
{ | |
//the function should not be marked as depricated | |
if (typeof cfg.funcs[i].dep === 'undefined' || cfg.funcs[i].dep == false) | |
{ | |
//check all sources of the function and only include it if it appears in the core | |
for (j in cfg.funcs[i].from) | |
{ | |
if (j.match(glcoregroup)) | |
{ | |
fcfg.funcs[i] = cfg.funcs[i]; | |
break; | |
} | |
} | |
} | |
} | |
cfg = fcfg; | |
//add coments so people can know suff about how the cfg was made | |
cfg['_comment'].push("BlueGin code generation cfg file"); | |
cfg['_comment'].push("Made with the help of gist.github.com/eekysam/ce93ea27fda816bdca91"); | |
//write out the result | |
fs.createWriteStream("gl.json").write(JSON.stringify(cfg, null, '\t')); | |
}); | |
}); | |
//these are the vendors, functions/enums that differ only in this are likly the same, so we want a regex | |
var vendors = "(ARB|KHR|OES|3DFX|AMD|APPLE|ATI|EXT|GREMEDY|HP|IBM|INGR|INTEL|MESAX|MESA|NVX|NV|OML|OVR|PGI|REND|S3|SGIS|SGIX|SGI|SUNX|SUN|WIN)" | |
//beware of kluges below | |
//out is the input C header file, and if dodep is true it marks everything new as depricated | |
function parse(out, dodep) | |
{ | |
//first remove any repeated spaces | |
out = out.replace(/ +/g, ' '); | |
//make all the newlines just \n | |
out = out.replace('\r', ''); | |
//split the lines | |
glext = out.split('\n'); | |
//how far are we in a C #ifdef thing | |
var ifdepth = 0; | |
//keep track of the "from" element of the func and enum cfgs that were just defined so we can add more data to them once we know their source | |
var funcfroms = []; | |
var enumfroms = []; | |
for (i in glext) | |
{ | |
//split the line by spaces | |
var def = glext[i].split(' '); | |
//we are entering a #ifdef thing | |
if (def[0] === '#ifdef' || def[0] === '#ifndef') | |
{ | |
ifdepth++; | |
} | |
//the #endif things tell us where the enums/functions above come from (what version/extention) | |
//the #ifdefs do too, but there are many #ifdefs are for other things and it is hard to tell | |
else if (def[0] === '#endif') | |
{ | |
ifdepth--; | |
//the #endifs we want should look like "#endif /* GL_VERSION_1_1 */" or similar (they need a 3rd word-a-ma-gig) | |
//#endifs for other things don't have that comment | |
if (def[2]) | |
{ | |
//that comment tells us what extention all the above stuff came from | |
var group = def[2]; | |
//if the groups list does not contain the extention we just found, add it (unless it starts with an underscore, then it is not an extention) | |
if (cfg.groups.indexOf(group) == -1 && !group.match(/^_/)) | |
{ | |
cfg.groups.push(group); | |
} | |
//the claz var is the LWJGL class the things can be found in | |
var claz = group; | |
var clazs = claz.split('_'); | |
//GL_VERSON_#_# things are in the GL## class | |
if (claz.match(glcoregroup)) | |
{ | |
claz = clazs[0] + clazs[2] + clazs[3]; | |
if (claz === 'GL10') | |
{ | |
claz = 'GL11'; | |
} | |
} | |
//Other classes are just camel-case versions of the extention name | |
else | |
{ | |
clazs.splice(0, 1); | |
claz = ''; | |
for(j in clazs) | |
{ | |
clazs[j] = clazs[j].capitalize(); | |
claz += clazs[j]; | |
} | |
} | |
//make sure all the functions just defined know what extentions/versions (groups) defined them | |
for (j in funcfroms) | |
{ | |
funcfroms[j].f[group] = funcfroms[j].g; | |
funcfroms[j].g['class'] = claz; | |
} | |
//we are about to start a new #ifdef, so clear out the list of unfinished funcs (they are finished now) | |
funcfroms = []; | |
//same for enums | |
for (j in enumfroms) | |
{ | |
enumfroms[j].f[group] = enumfroms[j].g; | |
} | |
enumfroms = []; | |
} | |
} | |
//We are adding a function | |
else if (def[0] === 'GLAPI') | |
{ | |
//the function name is the 3rd word with the first two letters (gl_____) removed | |
var name = def[3].substring(2).decapitalize(); | |
//remove the vendor name | |
name = name.replace(new RegExp("(.+)" + vendors + "$"), "$1"); | |
//Java enums and funtions can't start with a number, some gl enums and functions do | |
if (name.match(/^\d/)) | |
{ | |
//just prefix an underscore, it is ugly, but it works | |
//that stuff probibly won't be used anyway | |
//I can't belive I just said that | |
name = '_' + name; | |
} | |
//If the core or an extention has already added, start with that, otherwise initilize the stuff | |
var func = {}; | |
if (cfg.funcs[name]) | |
{ | |
func = cfg.funcs[name]; | |
} | |
else | |
{ | |
if (dodep) | |
{ | |
//its new and dodep is true, so it must be depricated | |
func.dep = true; | |
} | |
func.from = {}; | |
cfg.funcs[name] = func; | |
} | |
//the list of extentions/versions that add the function | |
var from = {}; | |
//the function call to LWJGL | |
from.call = def[3]; | |
//figure out what args it takes for arguments | |
var argss = ""; | |
for (var j = 4; j < def.length; j ++) | |
{ | |
argss += ' ' + def[j]; | |
} | |
//get rid of the brackets around the argument list | |
argss = argss.substring(2, argss.length - 2); | |
//if the arguments are void, there arn't any | |
if (argss === 'void') | |
{ | |
argss = []; | |
} | |
else | |
{ | |
argss = argss.split(', '); | |
for (j in argss) | |
{ | |
var a = argss[j]; | |
//this is supposed to make stuff nicer, not there yet | |
a = a.replace(/const ([^ \*]+) /, '$1 const'); | |
a = a.replace(/^GL([^ ]+ ) /, '$1 '); | |
argss[j] = a; | |
} | |
} | |
//make sure that if something already added this function, the args match up | |
if (!func.args || JSON.stringify(func.args) == JSON.stringify(argss)) | |
{ | |
func.args = argss; | |
func['return'] = def[1].replace(/GL(.+)/, '$1'); | |
//keep some references in storage so we can add its source version/extention later | |
funcfroms.push({f: func.from, g: from}); | |
} | |
} | |
//We are adding an enum | |
else if (def[0] === '#define') | |
{ | |
//all opengl enums start with GL_, we need to check this beause not all #define things are enums | |
if (def[1].indexOf('GL_') == 0) | |
{ | |
//get rid of the GL_ prefix and the vendor names | |
var name = def[1].substring(3).replace(new RegExp("_" + vendors + "$"), ""); | |
//we talked about this earlier | |
if (name.match(/^\d/)) | |
{ | |
name = '_' + name; | |
} | |
//some of the potential enums are accualy declaring avalible extentions/versions (not being enumish), we want to ignore them | |
var flag = false; | |
//all enums defined with hex are real enums (I think), some are not hex so more is needed | |
//all have uppercase names (should not match lowercase letters) | |
//all don't look like "GL_VERSION_#_#" | |
if (def[2].indexOf('0x') == 0 || !name.match(/[a-z]|(^VERSION_\d+_\d+$)/)) | |
{ | |
flag = true; | |
} | |
if (flag) | |
{ | |
//looks like an enum, so time to make it | |
var num = {}; | |
//if it has already been defined, start with that | |
if (cfg.enums[name]) | |
{ | |
num = cfg.enums[name]; | |
} | |
else | |
{ | |
if (dodep) | |
{ | |
num.dep = true; | |
} | |
num.from = {}; | |
cfg.enums[name] = num; | |
} | |
var n = def[2]; | |
//replace all the "this number is unsigned" stuff with none of that stuff | |
n = n.replace('u', ''); | |
//no multi-long-ness either, ONLY ONE L FOR U | |
n = n.replace(/l+/, 'l'); | |
//keep some references in storage so we can add its source version/extention later | |
enumfroms.push({f: num.from, g: n}); | |
} | |
} | |
} | |
} | |
} | |
// So ... A node.js script produces JSON based on a C header file, which is used by a Java app (compiled and run by gradle) to make more Java for a library. | |
// The end result is quite nice, but like a sausage, it is best not to think to much about what I have created. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment