widl-nan
Version:
Auto generate native C++ addon source code by parsing Web IDL definition
198 lines (162 loc) • 6.25 kB
JavaScript
// Copyright (c) 2016 Intel Corporation. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
;
const dot = require('dot');
const fs = require('fs.promised');
const path = require('path');
dot.templateSettings.strip = false; // Do not remove spaces & linebreaks
dot.log = process.env.WIDL_NAN_VERBOSE || false;
const dots = dot.process({path: path.join(path.dirname(__dirname), 'templates')});
// eslint-disable-next-line max-len, no-useless-escape
const ENUM_REGEX = /^\s*(typedef)*\s*(enum)\s*(\S*)\s*(\S*)\s*(:)*\s*(\S*)([\s\S\{](?!\}\s*\S*;))+\s*\}\s*(\S*)\s*;/gm;
var matchEnumRegex = {
// The regex
// And then define the index of regex groups
// Modify all these indices below, after any change of the regex above
numOfGroups: 8, enumGroup: 2,
// The followings work for: enum class type : int {...};
classGroup: 3, enumClassNameGroup: 4, colonGroup: 5, underlyingTypeGroup: 6,
// The followings work for:
// (1) typedef enum type {...} TYPE;
// (2) typedef enum {...} TYPE;
// (3) enum TYPE {...};
typdefKeywordGroup: 1, enumNameGroup: 3, curlyBracketGroup: 4, typedefTypeGroup: 8
};
function getEmptyEnumOption() {
var emptyEnumOption = {
name: undefined,
type: 'enum',
jsObjName: undefined,
cxxType: undefined,
cxxUnderlyingType: undefined,
defaultValue: undefined,
minValue: undefined,
maxValue: undefined,
source: null
};
return emptyEnumOption;
}
const _parseHeaderEnum = function(fileText) {
var result = [];
var match = /^namespace\s+(\S+)/m.exec(fileText);
var cxxNamespaceName = match ? (match[match.length - 1] || '') : '';
var extractedEnumArray = fileText.match(ENUM_REGEX);
if (!extractedEnumArray) {
console.log('Skipping (no enum definition found, or bad regex): ' + fileText);
}
var enumNameCollection = [];
extractedEnumArray.forEach(enumDef => {
// Remove comments
enumDef = enumDef.replace(/^\s*\/\/.*$/gm, '');
enumDef = enumDef.replace(/^\s*\/\*([\s\S](?!\*\/))+.*\*\//gm, '');
var enumOption = getEmptyEnumOption();
// eslint-disable-next-line max-len, no-useless-escape
var match = /^\s*(typedef)*\s*(enum)\s*(\S*)\s*(\S*)\s*(:)*\s*(\S*)([\s\S\{](?!\}\s*\S*;))+\s*\}\s*(\S*)\s*;/gm.exec(enumDef);
var enumName = '';
var underlyingTypeName = '';
if (match[matchEnumRegex.typdefKeywordGroup] === 'typedef') {
// typedef enum
enumName = match[matchEnumRegex.typedefTypeGroup];
underlyingTypeName = 'int';
} else if (match[matchEnumRegex.colonGroup] === ':') {
// enum class
enumName = match[matchEnumRegex.enumClassNameGroup];
underlyingTypeName = match[matchEnumRegex.underlyingTypeGroup];
} else if (!match[matchEnumRegex.typdefKeywordGroup] &&
match[matchEnumRegex.curlyBracketGroup] === '{') {
// enum
enumName = match[matchEnumRegex.enumNameGroup];
underlyingTypeName = 'int';
}
enumOption.name = enumOption.jsObjName = enumName;
enumOption.cxxUnderlyingType = underlyingTypeName;
if (cxxNamespaceName) {
enumOption.cxxType = cxxNamespaceName + '::' + enumName;
} else {
enumOption.cxxType = enumName;
}
// console.log('');
// console.log('enum: ' + enumName);
enumNameCollection.push(enumOption.name);
var enumData = {name: enumName, option: enumOption, values: []};
// Remove typedef enum class XXX : typename
enumDef = enumDef.replace(/^\s*(typedef)*\s*enum[^{]*\{/gm, '');
var lineTextArray = enumDef.split(/\n/);
var lastSeenEnumCounter = 0;
lineTextArray.forEach((lineText, index) => {
var rawName = '';
var rawValue = '';
// var lineMatch = /^\s*(\S+)\s*((:?=\s*([^,]*))|(:?\s*))\s*,?$/gm.exec(lineText);
// eslint-disable-next-line max-len
var lineMatch = /^\s*([A-Za-z_]{1,1}[A-Za-z0-9_]*)\s*((:?=\s*([^,^}^{]*))|(:?\s*))\s*,?/gm.exec(lineText);
if (lineMatch) {
rawName = lineMatch[1];
rawValue = lineMatch[4];
if (rawValue) {
// eslint-disable-next-line no-eval
lastSeenEnumCounter = eval(rawValue); // jshint ignore:line
rawValue = String(lastSeenEnumCounter);
} else {
// Counting mode
++lastSeenEnumCounter;
rawValue = String(lastSeenEnumCounter);
}
// console.log('Found enum definition: ' + rawName + ' = ' + rawValue);
enumOption.minValue = 0;
enumOption.maxValue = 0;
if (!enumOption.defaultValue) {
enumOption.defaultValue = enumOption.cxxType + '::' + rawName;
}
enumData.values.push({name: rawName, value: rawValue});
}
});
// console.log(enumData);
result.push(enumData);
// var json = dots.enumObj(enumData);
// console.log(enumData);
// var idl = dots.enumIDL(enumData);
// writeFile('./gen/gen-enum-' + enumOption.name + '.widl', idl);
// var jsonObj = JSON.parse(json);
// enumOption.source = jsonObj;
// var cxxSourceCode = dots.genEnumHeader(enumOption);
// writeFile('./gen/enum-' + enumOption.name + '.h', cxxSourceCode);
});
// console.log(enumNameCollection);
// writeFile('./gen/init_all_enum.h', dots.genAllEnumHeader(enumNameCollection));
// writeFile('./gen/test__all_enum.js', dots.genAllEnumHeaderTest(enumNameCollection));
return result;
};
const getCppEnumGenerator = function() {
var g = {
headerStore: [] // C++ header files
};
g.addFile = function(fileName) {
var that = this;
return fs.readFile(fileName)
.then(data => {
that.addText(data.toString(), fileName);
});
};
g.addText = function(str, name) {
this.headerStore.push({
name: name,
text: str,
definitions: []
});
};
g.compile = function() {
this.headerStore.forEach(header => {
header.definitions = _parseHeaderEnum(header.text);
});
};
g.writeTo = function(callback) {
this.headerStore.forEach(header => {
header.definitions.forEach(def => {
callback(header.name, dots.enumIDL(def));
});
});
};
return g;
};
module.exports = getCppEnumGenerator();