Skip to content

Instantly share code, notes, and snippets.

@samsartor
Last active August 29, 2015 14:25
Show Gist options
  • Save samsartor/ce93ea27fda816bdca91 to your computer and use it in GitHub Desktop.
Save samsartor/ce93ea27fda816bdca91 to your computer and use it in GitHub Desktop.
Help making the gl.json config for bluegin
//
// 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