t-code-generator
Version:
A Taf protocol proxy/servant generator
192 lines (173 loc) • 5.2 kB
JavaScript
const fs = require('fs')
const path = require('path')
const assert = require('assert')
const stripJsonComments = require('strip-json-comments')
const mustache = require('mustache')
const standard = require('standard')
const util = require('util')
const parser = require('../taf').parser
const localTNames = {
'bool': 'TModel.TBool',
'byte': 'TModel.TInt8',
'char': 'TModel.TInt8',
'short': 'TModel.TInt16',
'int': 'TModel.TInt32',
'long': 'TModel.TInt64',
'longlong': 'TModel.TInt64',
'ubyte': 'TModel.TUInt8',
'uchar': 'TModel.TUInt8',
'ushort': 'TModel.TUInt16',
'uint': 'TModel.TUInt32',
'string': 'TModel.TString',
'float': 'TModel.TFloat',
'double': 'TModel.TDouble'
}
const getTName = dataType => {
if (localTNames[dataType]) return localTNames[dataType]
let { name, proto, kproto, vproto } = dataType
if (!name) throw new Error(`type mismatch ${dataType}`)
switch (name) {
case 'vector':
if (proto === 'byte' || proto === 'char') {
return 'TModel.TBytes'
} else {
return `TModel.TList(${getTName(proto)})`
}
case 'map':
return `TModel.TMap(${getTName(kproto)}, ${getTName(vproto)})`
default:
throw new Error(`type mismatch ${JSON.stringify(dataType)}`)
}
}
const toKey = k => {
return {
structName: k.items[0],
items: k.items.slice(1)
}
}
const getDefaultValue = dataType => {
switch (dataType) {
case 'bool':
return 'false'
case 'byte':
case 'char':
case 'short':
case 'int':
case 'long':
case 'longlong':
case 'uchar':
case 'ubyte':
case 'ushort':
case 'uint':
case 'float':
case 'double':
return '0'
case 'string':
return '\'\''
default:
return 'null'
}
}
const toField = f => {
let { tag, require, dataType, name } = f
return {
name,
tag,
require,
tName: getTName(dataType),
defaultValue: getDefaultValue(dataType)
}
}
const toStruct = (s, keys) => {
let { name, fields } = s
localTNames[name] = name
return {
name,
fields: fields.map(toField),
key: keys.filter(k => k.structName === name)[0]
}
}
const toArg = a => {
let { dataType, name, tag } = a
return {
name,
tag,
tName: getTName(dataType),
defaultValue: getDefaultValue(dataType)
}
}
const toMethod = m => {
let { name, args } = m
args.forEach((a, index) => {
a.tag = index + 1
})
return {
name,
returnArg: toArg({ dataType: m.returnDataType, name: 'return' }),
inArgs: args.filter(a => !a.out).map(toArg),
outArgs: args.filter(a => a.out).map(toArg)
}
}
const toInterface = i => {
let { name, methods } = i
return {
name,
methods: methods.map(toMethod)
}
}
const toModule = m => {
let { name, content } = m
let keys = content.filter(c => c.type === 'key').map(toKey)
let structs = content.filter(c => c.type === 'struct').map(s => toStruct(s, keys))
.map(s => Object.assign(s, { moduleName: name }))
let interfaces = content.filter(c => c.type === 'interface').map(i => toInterface(i))
.map(i => Object.assign(i, { moduleName: name }))
return { name, structs, interfaces, hasKey: !!keys.length }
}
const toInclude = (i, filePath) => {
let includePath = path.resolve(path.dirname(filePath), i.path)
let includeData = parser.parse(stripJsonComments(fs.readFileSync(includePath, 'utf8')))
let moduleData = includeData.filter(d => d.type === 'module')[0]
let moduleName = moduleData.name
let moduleStructNames = moduleData.content.filter(d => d.type === 'struct').map(d => {
localTNames[d.name] = d.name
return d.name
})
let moduleInterfaceNames = moduleData.content.filter(d => d.type === 'interface').map(d => d.name)
let modulePath = i.path.replace(path.extname(i.path), '.js')
return { includePath, includeData, moduleName, moduleStructNames, moduleInterfaceNames, modulePath }
}
const gen = (filePath, recursive) => {
let data
try {
assert(filePath, `invalid filePath ${filePath}`)
data = parser.parse(stripJsonComments(fs.readFileSync(filePath, 'utf8')))
let includes = data.filter(d => d.type === 'include').map(i => toInclude(i, filePath))
let modules = data.filter(d => d.type === 'module').map(toModule)
let hasKey = !!modules.filter(m => m.hasKey).length
let hasInterface = !!modules.filter(m => m.interfaces.length).length
let code = mustache.render(fs.readFileSync(path.join(__dirname, 'template.mustache'), 'utf8'), {
includes,
modules,
hasKey,
hasInterface
})
standard.lintText(code, {
fix: true
}, (err, { results }) => {
if (err) throw err
let result = results[0]
let { messages, errorCount } = result
if (errorCount > 0) throw new Error(messages.map(m => m.message).join('\n'))
let { output } = result
fs.writeFileSync(filePath.replace(path.extname(filePath), '.js'), output || code, { encoding: 'utf8' })
})
if (recursive) {
includes.forEach(include => gen(include.includePath, recursive))
}
} catch (err) {
if (data) console.info(util.inspect(data, { depth: null }))
throw err
}
}
module.exports = gen