src/langs/cpp/combinator.js (1,790 lines of code) (raw):

'use strict'; const debug = require('../../lib/debug'); const CombinatorBase = require('../common/combinator'); const PackageInfo = require('./package_info'); const modules = require('./modules'); const { Symbol, } = require('../common/enum'); const { is, _symbol, _modify, _contain, _deepClone, _upperFirst, _toSnakeCase, _avoidKeywords, _resolveGrammerCall, _camelCase, _name } = require('../../lib/helper'); const Emitter = require('../../lib/emitter'); const { PropItem, AnnotationItem, GrammerNewObject, GrammerThrows, GrammerCatch, GrammerValue, GrammerCall, GrammerVar, Grammer, BehaviorToMap, TypeItem, TypeMap, TypeString, TypeGeneric, BehaviorTamplateString } = require('../common/items'); const assert = require('assert'); function _names(notes) { let nameMap = {}; if (notes['name']) { notes['name'].forEach(note => { if (note.prop !== note.value) { nameMap[note.prop] = note.value; } }); } return nameMap; } function _needRecur(prop_type) { if (is.undefined(prop_type) || !is.tree(prop_type)) { return false; } let itemType = prop_type.itemType ? prop_type.itemType : prop_type.valType; if (is.undefined(itemType)) { return false; } if (is.tree(itemType)) { return _needRecur.call(this, itemType); } if (is.object(itemType) && !this.isClient(itemType)) { return true; } return false; } class Combinator extends CombinatorBase { constructor(config, dependencies) { super(config, dependencies); this.eol = ';'; this.classNameMap = {}; this.package = _upperFirst(_camelCase(_name(this.config.name))); this.scope = _upperFirst(_camelCase(_name(this.config.scope))); this.namespace = `${this.scope}_${this.package}`; this.properties = {}; this.statements = {}; } addInclude(className) { let realFullClassName, includeFileName, using; const dependencies = this.dependencies; if (_contain(className, '$')) { realFullClassName = this.coreClass(className); includeFileName = `<${_toSnakeCase(this.config.tea.name)}/${_toSnakeCase(this.config.tea.header)}>`; } else if (dependencies[className]) { const info = dependencies[className]; // is third package let scope = _toSnakeCase(_name(info.scope)); let client = _toSnakeCase(_name(info.package_name)); includeFileName = `<${scope}/${client}.${this.config.header_ext}>`; realFullClassName = `${_upperFirst(_name(info.scope))}_${_upperFirst(_name(info.package_name))}::Client`; using = null; } else { debug.stack(`Class Name Error : ${className}`, dependencies); } if (!this.classNameMap[realFullClassName]) { const include = { import: realFullClassName, includeFileName, using }; this.classNameMap[realFullClassName] = include; this.includeList.push(include); } return realFullClassName; } addModelInclude(modelName) { let realFullClassName, includeFileName, using; let accessPath = modelName.split('.'); const dependencies = this.dependencies; if (_contain(modelName, '$')) { realFullClassName = this.coreClass(modelName); includeFileName = `<${_toSnakeCase(this.config.tea.name)}/${_toSnakeCase(this.config.tea.header)}>`; } else if (accessPath.length > 1 && dependencies[accessPath[0]]) { const info = dependencies[accessPath[0]]; // is third package model realFullClassName = `${_upperFirst(_name(info.scope)) }_${_upperFirst(_name(info.package_name)) }::${_name(accessPath.slice(1).join('.')) }`; let scope = _toSnakeCase(_name(info.scope)); let client = _toSnakeCase(_name(info.package_name)); includeFileName = `<${scope}/${client}.${this.config.header_ext}>`; using = null; } else if (accessPath.length === 1 && dependencies[accessPath[0]]) { const info = dependencies[accessPath[0]]; realFullClassName = `${_upperFirst(_name(info.scope)) }_${_upperFirst(_name(info.package_name)) }::Client`; let scope = _toSnakeCase(_name(info.scope)); let client = _toSnakeCase(_name(info.package_name)); includeFileName = `<${scope}/${client}.${this.config.header_ext}>`; using = null; } else { // self model realFullClassName = _name(accessPath.join('.')); includeFileName = `<${_toSnakeCase(this.scope)}/${_toSnakeCase(this.package)}.${this.config.header_ext}>`; using = null; } if (!this.classNameMap[realFullClassName]) { const include = { import: realFullClassName, includeFileName, using }; this.classNameMap[realFullClassName] = include; this.includeList.push(include); } return realFullClassName; } combine(objects = []) { if (this.config.packageInfo || this.config.exec) { const packageInfo = new PackageInfo(this.config, this.dependencies); packageInfo.emit(objects); } if (this.config.exec) { this.combineCode(objects); } else { this.combineHead(objects); this.combineCode(objects); } } combineHead(objects) { const models = objects.filter(obj => obj.type === 'model'); const [client] = objects.filter(obj => obj.type === 'client'); const outputPars = { head: '', body: '', foot: '' }; /******************************* emit body *******************************/ let emitter = new Emitter(this.config); emitter.emitln(`namespace ${this.namespace} {`); this.definedModels = []; this.models = models; // Fix the problem when used before definition models.forEach(model => this.emitModel(emitter, model)); this.emitClass(emitter, client); emitter.emitln(`} // namespace ${this.namespace}`); outputPars.body = emitter.output; /******************************* emit head *******************************/ emitter = new Emitter(this.config); if (client.topAnnotation.length > 0) { this.emitAnnotations(emitter, client.topAnnotation); emitter.emitln(); } const headerName = `${this.namespace}_H_`.toUpperCase(); emitter.emitln(`#ifndef ${headerName}`); emitter.emitln(`#define ${headerName}`).emitln(); this.emitInclude(emitter); outputPars.head = emitter.output; /******************************* emit foot *******************************/ emitter = new Emitter(this.config); emitter.emitln().emitln('#endif'); outputPars.foot = emitter.output; /***************************** combine output ******************************/ const config = _deepClone(this.config); config.ext = '.' + this.config.header_ext; config.dir = `${config.dir}/include/${_toSnakeCase(this.scope)}`; config.filename = _toSnakeCase(this.package); this.combineOutputParts(config, outputPars); } combineCode(objects) { const [object] = objects.filter(obj => obj.type === 'client'); object.name = 'Client'; const outputPars = { head: '', body: '', foot: '' }; this.object = object; this.properties = {}; object.body.filter(node => is.prop(node)).forEach(node => { this.properties[node.name] = { type: node.type, pointer: true }; }); /******************************* emit body *******************************/ let emitter = new Emitter(this.config); object.body.forEach(node => { this.statements = _deepClone(this.properties); if (is.func(node)) { this.emitFuncCode(emitter, node); } else if (is.construct(node)) { this.emitConstruct(emitter, node); } }); outputPars.body = emitter.output; /******************************* emit head *******************************/ emitter = new Emitter(this.config); if (object.topAnnotation.length > 0) { this.emitAnnotations(emitter, object.topAnnotation); emitter.emitln(); } if (!this.config.exec) { emitter.emitln(`#include <${_toSnakeCase(this.scope)}/${_toSnakeCase(this.package)}.${this.config.header_ext}>`); this.emitInclude(emitter); emitter.emitln(`using namespace ${this.namespace};`).emitln(); } else { this.emitInclude(emitter); } outputPars.head = emitter.output; /***************************** combine output ******************************/ const config = _deepClone(this.config); config.ext = '.' + this.config.code_ext; config.dir = `${config.dir}/src/`; config.filename = _toSnakeCase(this.package); this.combineOutputParts(config, outputPars); } emitInclude(emitter) { let includeFileSet = []; let includeList = this.includeList.concat(this.includeModelList); const selfInclude = `<${_toSnakeCase(this.scope)}/${_toSnakeCase(this.package)}.${this.config.header_ext}>`; if (includeList.length) { includeList.filter(item => item.includeFileName !== selfInclude).sort(function (a, b) { return a.includeFileName > b.includeFileName ? 1 : -1; }).forEach(include => { if (!_contain(includeFileSet, include.includeFileName)) { emitter.emitln(`#include ${include.includeFileName}`); includeFileSet.push(include.includeFileName); } }); emitter.emitln(); includeList = includeList.filter(item => !!item.using); const usingSet = []; if (includeList.length > 0) { includeList.forEach(item => { if (!_contain(usingSet, item.using)) { emitter.emitln(`using namespace ${item.using};`); usingSet.push(item.using); } }); emitter.emitln(); } } } emitModel(emitter, model) { const model_name = this.resolveName(model.name); if (_contain(this.definedModels, model_name)) { return; } if (model.subObject.length) { model.subObject.forEach(node => { this.emitModel(emitter, node); }); } model.body.filter(node => is.prop(node)).forEach(item => { if (is.object(item.type) && this.resolveName(item.type.objectName) === model_name) { return; } else if (is.array(item.type) && this.resolveName(item.type.itemType.objectName) === model_name) { return; } else if (is.map(item.type) && this.resolveName(item.type.valType.objectName) === model_name) { return; } this.findUndefinedModel(emitter, item.type, [model_name]); }); this.definedModels.push(model_name); this.emitClass(emitter, model); } emitClass(emitter, object) { /***************************** initialize include list and class name ******************************/ this.includeList = this.includeList.concat(object.includeList); this.includeModelList = this.includeModelList.concat(object.includeModelList); const className = object.name.split('.').map(item => _upperFirst(item)).join(''); /***************************** emit class header ******************************/ emitter.emit(`class ${className} `); if (object.type === 'model') { emitter.emit(`: public ${this.config.tea.model.name} `); } else if (object.extends.length) { emitter.emit(`: ${this.resolveName(object.extends[0])} `); } emitter.emitln('{'); /***************************** emit class body ******************************/ // emit common constructer let hasConstruct = false; if (object.type === 'model') { hasConstruct = true; } emitter.emitln('public:', this.level); this.levelUp(); // emit child nodes object.body.forEach(node => { if (is.prop(node)) { // emit properties emitter.emitln(`shared_ptr<${this.emitType(node.type)}> ${_avoidKeywords(node.name)}{};`, this.level); } else if (is.annotation(node)) { // emit annotation this.emitAnnotation(emitter, node); } else if (is.func(node)) { let modify = _modify(node.modify); let returnType = this.emitType(node.return[0], true); let func_name = _avoidKeywords(node.name); if (this.config.exec && func_name === 'main') { return; } if (modify) { emitter.emit(`${modify} ${returnType} ${func_name}(`, this.level); } else { emitter.emit(`${returnType} ${func_name}(`, this.level); } this.emitParams(emitter, node.params); emitter.emitln(');'); } else if (is.construct(node)) { // emit custom constructer if (node.params.length) { emitter.emit(`explicit ${className}(`, this.level); let tmp = []; node.params.forEach(param => { tmp.push(`const shared_ptr<${this.emitType(param.type, true)}>& ${_avoidKeywords(param.key)}`); this.addStatement(param.key, param.type, true); }); let emit = new Emitter(this.config); let str; if (tmp.length > 3) { let curr_row_len = emitter.currRow().length; str = tmp.join(`,${emit.eol}${' '.repeat(curr_row_len)}`); } else { str = tmp.join(', '); } emitter.emit(str); emitter.emit(')'); } else { emitter.emit(`${className}()`, this.level); } emitter.emitln(';'); hasConstruct = true; } }); if (object.type === 'model') { emitter.emitln(); this.emitModelFunc(emitter, className, object); } emitter.emitln(); if (!hasConstruct) { emitter.emitln(`${className}() {};`, this.level); } emitter.emitln(`virtual ~${className}() = default;`, this.level); this.levelDown(); /***************************** emit class footer ******************************/ emitter.emitln('};'); } emitModelFunc(emitter, className, object) { const notes = this.resolveNotes(object.body); emitter.emitln(`${className}() {}`, this.level); emitter.emitln(); emitter.emitln(`explicit ${className}(const std::map<string, boost::any> &config) : ${this.config.tea.model.name}(config) {`, this.level); this.levelUp(); emitter.emits(this.level, 'fromMap(config);' ); this.levelDown(); emitter.emitln('};', this.level); emitter.emitln(); // emit toMap&fromMap method let props = object.body.filter(node => is.prop(node)); this.emitValidate(emitter, props); this.emitToMap(emitter, props, notes); this.emitFromMap(emitter, props, notes); } emitValidate(emitter, props) { let emit = new Emitter(this.config); let validateNoteKeys = [ 'required', 'maximum', 'minimum', 'maxLength', 'minLength', // 'format', // 'enum', 'pattern', // 'maxItems' ]; this.levelUp(); props.forEach(prop => { prop.notes.forEach(note => { if (_contain(validateNoteKeys, note.key)) { let val = note.value; if (note.type === 'string') { val = `"${val}"`; } if (note.key === 'pattern') { val = val.split('\\').join('\\\\'); } if (note.key === 'required' && note.value === true) { emit.emitln(`if (!${_avoidKeywords(note.prop)}) {`, this.level); this.pushInclude('throw_exception', this.level); this.levelUp(); emit.emitln(`BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("${prop.name} is required.")));`, this.level); this.levelDown(); emit.emitln('}', this.level); } else if (note.key === 'maximum' || note.key === 'minimum') { let opt = note.key === 'maximum' ? '>' : '<'; emit.emitln(`if (${_avoidKeywords(note.prop)} && *${_avoidKeywords(note.prop)} ${opt} ${val}) {`, this.level); this.pushInclude('throw_exception', this.level); this.levelUp(); emit.emitln(`BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("${prop.name} is required.")));`, this.level); this.levelDown(); emit.emitln('}', this.level); } else if (note.key === 'pattern') { if (is.string(prop.type)) { emit.emitln(`${this.addInclude('$Model')}::validatePattern("${_avoidKeywords(note.prop)}", ${_avoidKeywords(note.prop)}, ${val});`, this.level); } else { debug.warning(`Only supported string for validate pattern. ${prop.getParent().name}::${prop.name} type is ${this.emitType(prop.type)}.`); } } else { emit.emitln(`${this.addInclude('$Model')}::validate${_upperFirst(note.key)}("${_avoidKeywords(note.prop)}", ${_avoidKeywords(note.prop)}, ${val});`, this.level); } } }); }); this.levelDown(); if (emit.output) { emitter.emitln('void validate() override {', this.level); emitter.emit(emit.output); emitter.emitln('}', this.level); } else { emitter.emitln('void validate() override {}', this.level); } emitter.emitln(); } emitMakeShared(type, data, need_cast = false) { let type_str = typeof (type) === 'string' ? type : this.emitType(type); if (type_str === 'nullptr') { return 'nullptr'; } let data_str; if (typeof (data) === 'string') { data_str = data; } else { const emit = new Emitter(this.config); this.grammer(emit, data, false, false); data_str = emit.output; } if (need_cast) { return `make_shared<${type_str}>(boost::any_cast<${type_str}>(${data_str}))`; } return `make_shared<${type_str}>(${data_str})`; } emitToMapProp(emitter, prefix = 'res', prop, layer = 1, parent_type) { let var_name = ''; let isProp = layer === 1 && !this.isClient(prop); if (isProp) { var_name = `*${_avoidKeywords(prop.name)}`; emitter.emitln(`if (${_avoidKeywords(prop.name)}) {`, this.level); this.levelUp(); } else { var_name = _avoidKeywords(prop.name); } if (_needRecur.call(this, prop.type)) { // item type is array or map const p = new PropItem(); p.type = is.array(prop.type) ? prop.type.itemType : prop.type.valType; let pre = ''; let temp = ''; if (is.array(prop.type)) { p.name = `item${layer}`; temp = `temp${layer}`; pre = `${temp}`; } else { p.name = `item${layer}.second`; temp = `temp${layer}`; pre = `${temp}[item${layer}.first]`; } let temp_type = is.array(prop.type) ? 'vector<boost::any>' : 'map<string, boost::any>'; emitter.emitln(`${temp_type} ${temp};`, this.level); emitter.emitln(`for(auto item${layer}:${var_name}){`, this.level); this.levelUp(); this.emitToMapProp(emitter, pre, p, layer + 1, prop.type); this.levelDown(); emitter.emitln('}', this.level); if (is.array(prop.type)) { emitter.emitln(`${prefix} = boost::any(${temp});`, this.level); } else { emitter.emitln(`${prefix} = boost::any(${temp});`, this.level); } } else if (is.object(prop.type)) { // type is model if (!this.isClient(prop)) { // is not client if (layer === 1) { emitter.emitln(`${prefix} = ${_avoidKeywords(prop.name)} ? boost::any(${_avoidKeywords(prop.name)}->toMap()) : boost::any(map<string,boost::any>({}));`, this.level); } else { if (is.array(parent_type)) { emitter.emitln(`${prefix}.push_back(boost::any(${_avoidKeywords(prop.name)}.toMap()));`, this.level); } else { emitter.emitln(`${prefix} = boost::any(${_avoidKeywords(prop.name)}.toMap());`, this.level); } } } else { debug.warning('sub item type is client : ' + prefix); } } else if (is.base(prop.type)) { // is base type emitter.emitln(`${prefix} = boost::any(${var_name});`, this.level); } else { debug.warning('unsupported type toMap()', prop); } if (isProp) { this.levelDown(); emitter.emitln('}', this.level); } } emitToMap(emitter, props, notes) { let nameMap = _names(notes); emitter.emitln('map<string, boost::any> toMap() override {', this.level); this.levelUp(); emitter.emitln('map<string, boost::any> res;', this.level); props.forEach(prop => { let name = typeof nameMap[prop.name] !== 'undefined' ? nameMap[prop.name] : prop.name; this.emitToMapProp(emitter, `res["${name}"]`, prop); }); emitter.emitln('return res;', this.level); this.levelDown(); emitter.emitln('}', this.level); emitter.emitln(); } emitFromMapItem(emitter, target, type, source, parent_type = null, layer = 1) { if (is.array(type)) { emitter.emitln(`vector<${this.emitType(type.itemType)}> toVec${layer};`, this.level); emitter.emitln(`if (typeid(vector<boost::any>) == ${source}.type()) {`, this.level); this.levelUp(); emitter.emitln(`vector<boost::any> vec${layer} = boost::any_cast<vector<boost::any>>(${source});`, this.level); emitter.emitln(`for (auto item:vec${layer}) {`, this.level); this.levelUp(); this.emitFromMapItem(emitter, ` toVec${layer}`, type.itemType, 'item', type, layer + 1); this.levelDown(); emitter.emitln('}', this.level); this.levelDown(); emitter.emitln('}', this.level); if (layer === 1) { emitter.emitln(`${target} = ${this.emitMakeShared(type, `toVec${layer}`)};`, this.level); } else { emitter.emitln(`${target} = toVec${layer};`, this.level); } } else if (is.map(type)) { emitter.emitln(`map<${this.emitType(type.keyType)}, ${this.emitType(type.valType)}> map${layer} = boost::any_cast<map<${this.emitType(type.keyType)}, ${this.emitType(type.valType)}>>(${source});`, this.level); emitter.emitln(`map<${this.emitType(type.keyType)}, ${this.emitType(type.valType)}> toMap${layer};`, this.level); emitter.emitln(`for (auto item:map${layer}) {`, this.level); this.levelUp(); this.emitFromMapItem(emitter, ` toMap${layer}[item.first]`, type.valType, 'item.second', type, layer + 1); this.levelDown(); emitter.emitln('}', this.level); if (layer === 1) { emitter.emitln(`${target} = ${this.emitMakeShared(type, `toMap${layer}`)};`, this.level); } else if (is.array(parent_type)) { emitter.emitln(`${target}.push_back(toMap${layer});`, this.level); } else { emitter.emitln(`${target} = toMap${layer};`, this.level); } } else if (is.stream(type)) { if (is.array(parent_type)) { emitter.emitln(`${target}.push_back(boost::any_cast<${this.addInclude('$Stream')}>(${source}));`, this.level); } else if (layer === 1) { emitter.emitln(`${target} = ${this.emitMakeShared(this.addInclude('$Stream'), source, true)};`, this.level); } else { emitter.emitln(`${target} = boost::any_cast<${this.addInclude('$Stream')}>(${source});`, this.level); } } else if (is.base(type)) { if (is.array(parent_type)) { emitter.emitln(`${target}.push_back(boost::any_cast<${this.emitType(type)}>(${source}));`, this.level); } else if (layer === 1) { emitter.emitln(`${target} = ${this.emitMakeShared(type, source, true)};`, this.level); } else { emitter.emitln(`${target} = ${source};`, this.level); } } else if (is.object(type)) { if (is.array(parent_type)) { if (layer === 1) { emitter.emitln(`${target}->push_back(${source});`, this.level); } else { emitter.emitln(`${target}.push_back(${source});`, this.level); } } else if (layer === 1) { emitter.emitln(`${target} = ${this.emitMakeShared(type, source)};`, this.level); } else { emitter.emitln(`${target} = ${this.emitType(type)}(${source});`, this.level); } } else { debug.stack(target, type, source); } } emitFromMapProp(emitter, name, prop, realkey, layer = 1, parent_type = null) { const isProp = realkey.indexOf('expect') !== 0 && !this.isClient(prop); if (isProp) { emitter.emitln(`if (m.find("${realkey}") != m.end() && !m["${realkey}"].empty()) {`, this.level); this.levelUp(); } if (_needRecur.call(this, prop.type)) { // item type is array or map const p = new PropItem(); p.type = is.array(prop.type) ? prop.type.itemType : prop.type.valType; let nextName = ''; let nextExpectName = ''; let expectName = `expect${layer}`; if (is.array(prop.type)) { p.name = `item${layer}`; p.parent = prop.type; nextName = `item${layer}`; nextExpectName = `${expectName}`; } else { p.name = `item${layer}.second`; nextName = `item${layer}.second`; nextExpectName = `${expectName}[item${layer}.first]`; } let temp_type = is.array(prop.type) ? 'vector<boost::any>' : 'map<string, boost::any>'; let expected_type = this.emitType(prop.type); emitter.emitln(`if (typeid(${temp_type}) == ${name}.type()) {`, this.level); this.levelUp(); emitter.emitln(`${expected_type} ${expectName};`, this.level); emitter.emitln(`for(auto item${layer}:boost::any_cast<${temp_type}>(${name})){`, this.level); this.levelUp(); this.emitFromMapProp(emitter, nextName, p, nextExpectName, layer + 1, prop.type); this.levelDown(); emitter.emitln('}', this.level); if (prop.parent && is.array(prop.parent)) { if (layer === 1) { emitter.emitln(`${realkey}.push_back(${this.emitMakeShared(prop.type, expectName)};`, this.level); } else { emitter.emitln(`${realkey}.push_back(${expectName});`, this.level); } } else { if (layer === 1) { emitter.emitln(`${_avoidKeywords(prop.name)} = ${this.emitMakeShared(prop.type, expectName)};`, this.level); } else { if (is.array(parent_type)) { emitter.emitln(`${realkey}.push_back(${expectName});`, this.level); } else { emitter.emitln(`${realkey} = ${expectName};`, this.level); } } } this.levelDown(); emitter.emitln('}', this.level); } else if (is.object(prop.type)) { if (!this.isClient(prop)) { // type is model let var_name = `model${layer}`; emitter.emitln(`if (typeid(map<string, boost::any>) == ${name}.type()) {`, this.level); this.levelUp(); emitter.emitln(`${this.emitType(prop.type)} ${var_name};`, this.level); emitter.emitln(`${var_name}.fromMap(boost::any_cast<map<string, boost::any>>(${name}));`, this.level); if (isProp) { this.emitFromMapItem(emitter, _avoidKeywords(prop.name), prop.type, var_name, parent_type, layer); } else { if (is.array(parent_type)) { emitter.emitln(`${realkey}.push_back(${var_name});`, this.level); } else { emitter.emitln(`${realkey} = ${var_name};`, this.level); } } this.levelDown(); emitter.emitln('}', this.level); } else { // type is client debug.warning('sub item type is client : ' + prop.name); } } else if (is.base(prop.type)) { if (isProp) { this.emitFromMapItem(emitter, _avoidKeywords(prop.name), prop.type, name); } else { this.emitFromMapItem(emitter, realkey, prop.type, name, parent_type, layer); } } else { debug.warning('unsupported type fromMap()', prop); } if (isProp) { this.levelDown(); emitter.emitln('}', this.level); } } emitFromMap(emitter, props, notes) { let nameMap = _names(notes); emitter.emitln('void fromMap(map<string, boost::any> m) override {', this.level); this.levelUp(); props.forEach(prop => { let name = typeof nameMap[prop.name] !== 'undefined' ? nameMap[prop.name] : prop.name; this.emitFromMapProp(emitter, `m["${name}"]`, prop, name); }); this.levelDown(); emitter.emitln('}', this.level); emitter.emitln(); } emitParams(emitter, params = []) { let tmp = []; params.forEach(param => { if (is.any(param.type)) { tmp.push(`const boost::any &${_avoidKeywords(param.key)}`); this.addStatement(param.key, param.type, false); } else { tmp.push(`shared_ptr<${this.emitType(param.type)}> ${_avoidKeywords(param.key)}`); this.addStatement(param.key, param.type, true); } }); let emit = new Emitter(this.config); let str; if (tmp.length > 3) { let curr_row_len = emitter.currRow().length; str = tmp.join(`,${emit.eol}${' '.repeat(curr_row_len)}`); } else { str = tmp.join(', '); } if (emitter instanceof Emitter) { emitter.emit(str); } return str; } emitType(type, autoPtr = false) { if (!is.type(type)) { debug.stack('Inavalid type', type); } let type_str = null; if (is.any(type)) { this.pushInclude('boost_any'); type_str = 'boost::any'; } else if (is.decimal(type)) { type_str = 'double'; } else if (is.integer(type)) { type_str = type.length > 16 ? 'long' : 'int'; } else if (is.number(type)) { type_str = 'int'; } else if (is.string(type)) { this.pushInclude('iostream'); type_str = 'string'; } else if (is.bytes(type)) { this.pushInclude('vector'); type_str = 'vector<uint8_t>'; } else if (is.array(type)) { let subType = this.emitType(type.itemType); this.pushInclude('vector'); type_str = `vector<${subType}>`; } else if (is.bool(type)) { type_str = 'bool'; } else if (is.void(type)) { type_str = 'void'; } else if (is.map(type)) { this.pushInclude('map'); type_str = `map<${this.emitType(type.keyType)}, ${this.emitType(type.valType)}>`; } else if (is.stream(type)) { type_str = this.addInclude('$Stream'); } else if (is.object(type)) { type_str = !type.objectName ? 'void' : this.resolveName(type.objectName); } else if (is.null(type)) { type_str = 'nullptr'; } if (type_str === null) { debug.stack('Unsupported Type', type); } if (autoPtr && this.isPointerType(type)) { type_str = `shared_ptr<${type_str}>`; } return type_str; } emitAnnotation(emitter, annotation, level) { if (typeof level === 'undefined') { level = this.level; } if (annotation.mode === 'single') { emitter.emitln(`// ${annotation.content}`, level); } else if (annotation.mode === 'multi') { emitter.emitln('/**', level); annotation.content.forEach(c => { emitter.emitln(` * ${c}`, level); }); emitter.emitln(' */', level); } else { debug.stack('Unsupported annotation.mode :' + annotation.mode, annotation); } } emitConstruct(emitter, node) { if (!node.params.length && !node.body.length) { emitter.emitln(`${this.namespace}::Client::Client(){}`, this.level); return; } // has super call const hasSuperCall = function (item) { if (item instanceof GrammerValue && item.type === 'call') { return hasSuperCall(item.value); } return item instanceof GrammerCall && item.type === 'super'; }; const hasSuper = node.body.some(item => hasSuperCall(item)); // emit construct header emitter.emit(`${this.namespace}::Client::Client(`); if (node.params.length) { let tmp = []; node.params.forEach(param => { tmp.push(`const shared_ptr<${this.emitType(param.type)}>& ${_avoidKeywords(param.key)}`); this.addStatement(param.key, param.type, true); }); let emit = new Emitter(this.config); let str; if (tmp.length > 3) { let curr_row_len = emitter.currRow().length; str = tmp.join(`,${emit.eol}${' '.repeat(curr_row_len)}`); } else { str = tmp.join(', '); } if (emitter instanceof Emitter) { emitter.emit(str); } emitter.emit(')', this.level); if (hasSuper) { let tmp = []; node.params.forEach(p => { tmp.push(p.key); }); emitter.emit(` : ${this.resolveName(this.object.extends[0])}(${tmp.join(', ')})`); } } else { emitter.emit(')'); } const nodes = node.body.filter(node => !hasSuperCall(node)); if (nodes.length) { emitter.emitln(' {'); this.levelUp(); node.body.filter(node => !hasSuperCall(node)).forEach(node => { this.grammer(emitter, node); }); this.levelDown(); emitter.emitln('};', this.level); emitter.emitln(); } else { emitter.emitln(' {};'); } } emitFuncCode(emitter, func) { this.funcReturnType = func.return[0]; let func_name = _avoidKeywords(func.name); func_name = this.config.exec ? func_name : `${this.namespace}::Client::${func_name}`; if (this.config.exec && func.name === 'main') { const argv = func.params[0].key; emitter.emitln(`int main(int argc, char *${argv}[]) {`); this.levelUp(); emitter.emitln(`${argv}++;`, this.level); } else { if (func.params.length) { emitter.emit(`${this.emitType(func.return[0], true)} ${func_name}(`); this.emitParams(emitter, func.params); emitter.emit(')'); } else { emitter.emit(`${this.emitType(func.return[0], true)} ${func_name}()`); } emitter.emitln(' {'); this.levelUp(); } if (this.config.manual && func.body.length === 1 && func.body[0] instanceof GrammerThrows) { emitter.emitln('return "";', this.level); } else { func.body.forEach(node => { this.grammer(emitter, node); }); } this.levelDown(); emitter.emitln('}', this.level); emitter.emitln(); } /**************************************** analyze ****************************************/ findUndefinedModel(emitter, type, parentObject = []) { const models = this.models; if (is.object(type)) { let objectName = this.resolveName(type.objectName); if (parentObject.indexOf(objectName) < 0) { parentObject.push(objectName); } if (!_contain(this.definedModels, objectName)) { const [obj] = models.filter(node => node.name === objectName); if (obj) { obj.body.filter(node => is.prop(node)).forEach(item => { if (is.object(item.type)) { const typeObjectName = this.resolveName(item.type.objectName); if (typeObjectName === objectName) { return; } else if (parentObject.indexOf(typeObjectName) < 0) { parentObject.push(typeObjectName); } } this.findUndefinedModel(emitter, item.type, parentObject); }); this.emitModel(emitter, obj); } } } else if (is.array(type)) { if (is.object(type.itemType)) { const objectName = this.resolveName(type.itemType.objectName); if (parentObject && parentObject.indexOf(objectName) > -1) { return; } parentObject.push(objectName); this.findUndefinedModel(emitter, type.itemType, parentObject); } else { this.findUndefinedModel(emitter, type.itemType, parentObject); } } else if (is.map(type)) { if (is.object(type.valType)) { const objectName = this.resolveName(type.valType.objectName); if (parentObject && parentObject.indexOf(objectName) > -1) { return; } parentObject.push(objectName); this.findUndefinedModel(emitter, type.valType, parentObject); } else { this.findUndefinedModel(emitter, type.valType, parentObject); } } return parentObject; } isClient(prop) { let client_name = prop.objectName ? prop.objectName : prop.type.objectName; client_name = this.resolveName(client_name); if (client_name && client_name.indexOf('Client') < 0) { return false; } return Object.keys(this.dependencies).some((key) => { const item = this.dependencies[key]; let namespace = `${_upperFirst(_name(item.scope))}_${_upperFirst(_name(item.package_name))}::Client`; return namespace === client_name; }); } pushInclude(includeName) { const include = this.config.third[includeName]; if (!this.includeList.some(item => include.import === item.import)) { this.includeList.push(include); } } isPointerPath(lastPathIndex, paths) { const lastPath = paths[lastPathIndex]; if (lastPath.type === 'object') { let name = _name(lastPath.name); return this.isPtrStatement(name); } else if (lastPath.type === 'parent') { return true; } if (lastPath.type === 'prop') { if (lastPathIndex > 0) { let parentPath = paths[lastPathIndex - 1]; if (parentPath.type === 'object') { let parentType = this.getStatementType(_name(parentPath.name)); if (parentType.objectName === '$Request' || parentType.objectName === '$Response') { if (lastPath.name === 'body') { return true; } return false; } } } return true; } return false; } isUnsetMethod(gram) { return gram.type === 'sys_func' && gram.path[0].name === '^Util' && gram.path[1].name === 'isUnset'; } useTmplMethod(gram) { const useTmplMethods = ['isUnset', 'toArray']; return gram.type === 'sys_func' && gram.path[0].name === '^Util' && _contain(useTmplMethods, gram.path[1].name); } resolveCallPath(paths, params = '', gram) { let prefix = ''; let lastPathIndex = -1; paths.forEach((path, i) => { let pathName = typeof path.name === 'string' ? path.name.replace('@', '_') : `${path.name}`; pathName = _name(pathName); if (path.type === 'parent') { if (paths[i + 1] && _contain(paths[i + 1].type, 'static')) { prefix += `${this.config.client.name}`; if (path.name) { prefix += '::' + pathName; } } else { prefix += ''; if (path.name) { prefix += `${pathName}`; } } } else if (path.type === 'object' || path.type === 'object_static') { prefix += this.resolveName(pathName); } else if (path.type === 'call') { let isPointer = this.isPointerPath(lastPathIndex, paths); prefix += isPointer ? `->${pathName}(${params})` : `.${pathName}(${params})`; } else if (path.type === 'call_static') { if (this.config.exec && prefix === 'Client') { if (this.useTmplMethod(gram)) { prefix = `${pathName}<${this.emitType(gram.params[0].dataType)}>(${params})`; } else { prefix = `${pathName}(${params})`; } } else { if (this.useTmplMethod(gram)) { prefix += `::${pathName}<${this.emitType(gram.params[0].dataType)}>(${params})`; } else { prefix += `::${pathName}(${params})`; } } } else if (path.type === 'prop') { let isPointer = this.isPointerPath(lastPathIndex, paths); prefix += isPointer ? `->${pathName}` : `.${pathName}`; } else if (path.type === 'prop_static') { prefix += `::${pathName}`; } else if (path.type === 'map') { let isPointer = this.isPointerPath(lastPathIndex, paths); let path_name; if (path.isVar) { let varIsPointer = this.isPtrStatement(pathName); if (varIsPointer) { path_name = `*${pathName}`; } else { path_name = pathName; } } else if (path.name instanceof Grammer) { path_name = super.resolveName(path.name); } else { path_name = `"${pathName}"`; } if (isPointer) { prefix = `(*${prefix})[${path_name}]`; } else { prefix += `[${path_name}]`; } } else if (path.type === 'list') { let isPointer = this.isPointerPath(lastPathIndex, paths); let path_name = pathName; if (path.isVar) { let varIsPointer = this.isPtrStatement(pathName); if (varIsPointer) { path_name = `*${pathName}`; } else { path_name = pathName; } } else if (path.name instanceof Grammer) { path_name = super.resolveName(path.name); } if (isPointer) { prefix = `(*${prefix})[${path_name}]`; } else { prefix += `[${path_name}]`; } } else { debug.stack(paths); } lastPathIndex = i; }); if (prefix[0] === '.') { prefix = prefix.slice(1); } else if (prefix[0] === ':') { prefix = prefix.slice(2); } else if (prefix[0] === '-') { prefix = prefix.slice(2); } return prefix; } resolveParams(gram) { let tmp = []; gram.params.forEach(p => { let emit = new Emitter(this.config); let isUnsetMethod = this.isUnsetMethod(gram); if (p.value instanceof BehaviorToMap && gram.type === 'sys_func' && isUnsetMethod) { let isPointer = this.isPointerVar(p, true); if (!isPointer) { emit.emit(this.emitMakeShared(p.dataType, p.value.grammer)); } else { this.grammer(emit, p.value.grammer, false, false); } tmp.push(emit.output); } else if (p.value instanceof BehaviorToMap && gram.type === 'sys_func') { tmp.push(this.emitMakeShared('map<string, boost::any>', p)); } else if (gram.type === 'sys_func' && isUnsetMethod) { this.grammer(emit, p, false, false); tmp.push(emit.output); } else if (gram.type === 'sys_func') { if (!this.isPointerVar(p)) { emit.emit(this.emitMakeShared(p.dataType, p)); } else { this.grammer(emit, p, false, false); } tmp.push(emit.output); } else if (p instanceof GrammerVar && p.type.objectName === '$Exception') { this.grammer(emit, p, false, false); tmp.push(`${emit.output}`); } else if (p instanceof GrammerValue && p.type === 'map' && p.needCast) { this.pushInclude('darabonba_core'); emit.emit(`${this.config.tea.converter.name}::mapPointer(`); this.grammerValue(emit, p); emit.emit(')'); tmp.push(emit.output); } else if (p.type === 'var' && is.object(p.value.type) && p.value.varType === 'static_class') { tmp.push('nullptr'); } else if (p.type === 'param') { if (this.isPtrStatement(p.value)) { tmp.push(p.value); } else { tmp.push(this.emitMakeShared(p.dataType, p.value)); } } else if (!this.isPointerVar(p)) { this.grammer(emit, p, false, false); let dataType = this.resolveDataType(p); if (is.stream(p.dataType)) { tmp.push(emit.output); } else if (p.type === 'behavior' && p.value instanceof BehaviorToMap) { tmp.push(this.emitMakeShared('map<string, boost::any>', emit.output)); } else if (!this.isPointerVar(p)) { tmp.push(this.emitMakeShared(dataType, emit.output)); } else { tmp.push(emit.output); } } else { this.grammerValue(emit, p); tmp.push(emit.output); } }); return tmp.join(', '); } resolveDataType(gram) { let expectedType = null; if (gram.returnType) { expectedType = gram.returnType; } else if (gram.dataType) { expectedType = gram.dataType; } else if (gram.type instanceof TypeItem) { expectedType = gram.type; } else if (gram.type === 'call') { expectedType = gram.value.returnType; } if (expectedType === null) { return null; } return this.emitType(expectedType); } resolveName(name, _avoidKeywords = true) { name = super.resolveName(name, _avoidKeywords); if (_contain(name, '.')) { name = name.split('.').map(s => _upperFirst(s)).join(''); } return name; } isPointerType(type) { if (is.stream(type)) { return true; } return false; } isPointerVar(gram, ignore_to_map = false) { if (gram instanceof GrammerValue) { if (gram.type === 'null') { return false; } } let grammerVar; if (gram instanceof GrammerVar) { grammerVar = gram; } if (gram instanceof GrammerValue) { if (gram.type === 'var' && gram.value instanceof GrammerVar) { grammerVar = gram.value; } else if (gram.type === 'call' && gram.value instanceof GrammerCall) { grammerVar = gram.value; } else if (ignore_to_map && gram.type === 'behavior' && gram.value instanceof BehaviorToMap) { grammerVar = gram.value.grammer; } } if (grammerVar && grammerVar instanceof GrammerVar) { let name = grammerVar.name ? _name(grammerVar.name) : ''; if (this.isPtrStatement(name)) { return true; } } else if (grammerVar instanceof GrammerCall) { return this.isPointerPath(grammerVar.path.length - 1, grammerVar.path); } return false; } addStatement(name, type, pointer = null) { if (null === pointer) { pointer = this.isPointerType(type); } this.statements[name] = { type, pointer }; } isPtrStatement(name) { return this.statements[name] && this.statements[name].pointer; } hasStatement(name) { return !this.statements[name] ? false : true; } getStatementType(name) { if (!this.statements[name]) { debug.stack('Undefined statement!', name, this.statements); } return this.statements[name].type; } /**************************************** grammer ****************************************/ grammerVar(emitter, gram, emitType = true) { let name = gram.name ? gram.name : gram.key; name = _name(name); if (gram.varType === 'static_class') { emitter.emit(`${name}::class`); } else if (gram.varType === 'var' || gram.varType === 'const') { if (!this.hasStatement(name) && emitType) { emitter.emit(`shared_ptr<${this.emitType(gram.type)}> ${name}`); this.addStatement(name, gram.type, true); } else { emitter.emit(`${name}`); } if (!this.hasStatement(name)) { this.addStatement(name, gram.type); } } else { debug.stack(gram); } } emitMap(emitter, gram, layer = 0) { this.pushInclude('map'); let items = []; let expandItems = []; let keyType = this.emitType(gram.dataType.keyType); let valType = this.emitType(gram.dataType.valType); if (!Array.isArray(gram.value) && !(gram.value instanceof GrammerValue)) { this.grammer(emitter, gram.value, false, false); return; } items = gram.value.filter(i => !i.isExpand); expandItems = gram.value.filter(i => i.isExpand); if (!items.length && !expandItems.length) { emitter.emit(`map<${keyType}, ${valType}>()`); return; } let needCast = gram.needCast; if (needCast) { this.pushInclude('darabonba_core'); emitter.emit(`${this.config.tea.converter.name}::merge(`); } if (!items.length && expandItems.length) { emitter.emit(`map<${keyType}, ${valType}>(), `); } // emit map if (items.length) { if (gram.needCast || layer !== 0) { emitter.emitln(`map<${keyType}, ${valType}>({`); } else { if (this.exprIsAssignToPtr) { emitter.emitln(`map<${keyType}, ${valType}>({`); } else { emitter.emitln('{'); } } this.levelUp(); items.forEach((item, i) => { if (item instanceof AnnotationItem) { this.emitAnnotation(emitter, item); return; } emitter.emit(`{"${item.key}", `, this.level); let isAny = is.any(gram.dataType.valType); if (this.isPointerVar(item)) { let emit = new Emitter(this.config); this.grammerValue(emit, item, layer + 1); let emitItem = emit.output; if (isAny) { emitter.emit(`!${emitItem} ? boost::any() : boost::any(*${emitItem})`); } else { emitter.emit(`!${emitItem} ? ${this.emitType(gram.dataType.valType)}() : *${emitItem}`); } } else { if (isAny) { if (is.any(item.dataType)) { this.grammerValue(emitter, item, layer + 1); } else { emitter.emit('boost::any('); if (is.string(item.dataType)) { if (item.type === 'behavior' && item.value instanceof BehaviorTamplateString) { this.grammerValue(emitter, item, layer + 1); } else { emitter.emit('string('); this.grammerValue(emitter, item, layer + 1); emitter.emit(')'); } } else { this.grammerValue(emitter, item, layer + 1); } emitter.emit(')'); } } else { this.grammerValue(emitter, item, layer + 1); } } if (i < items.length - 1) { emitter.emitln('},'); } else { emitter.emitln('}'); } }); this.levelDown(); if (gram.needCast || layer !== 0) { emitter.emit('})', this.level); } else { if (this.exprIsAssignToPtr) { emitter.emitln('})', this.level); } else { emitter.emit('}', this.level); } } if (expandItems.length) { emitter.emit(', '); } } if (expandItems.length) { let tmp = []; expandItems.forEach(item => { let emit = new Emitter(this.config); let needConvertMap = is.map(item.dataType) && is.any(gram.dataType.valType) && !is.any(item.dataType.valType); if (needConvertMap) { emit.emit(`${this.config.tea.converter.name}::toGenericMap(`); } if (this.isPointerVar(item)) { let varEmit = new Emitter(this.config); this.grammer(varEmit, item, false, false); let var_name = varEmit.output; let type_name = this.resolveDataType(item); if (is.object(item.dataType)) { type_name = `map<${keyType}, ${valType}>`; } emit.emit(`!${var_name} ? ${type_name}() : ${is.object(item.dataType) ? `${var_name}.toMap()` : `*${var_name}`}`); } else if (item.type === 'var') { this.grammer(emit, item, false, false); } else if (item.type === 'call') { this.grammer(emit, item, false, false); } else { emit.emit(`map<${keyType}, ${valType}>(`); this.grammer(emit, item, false, false); emit.emit(')'); } if (needConvertMap) { emit.emit(')'); } tmp.push(emit.output); }); emitter.emit(tmp.join(', ')); } if (needCast) { // emitter.emit('))'); emitter.emit(')'); // close converter merge method } } grammerValue(emitter, gram, layer = 0) { if (is.annotation(gram)) { this.emitAnnotation(emitter, gram); return; } if (gram.type === 'map' || gram.type === 'model_construct_params') { if (gram.type === 'model_construct_params' && this.emitType(gram.dataType) !== 'map<string, boost::any>') { gram.dataType = new TypeMap( new TypeString(), new TypeGeneric() ); } this.emitMap(emitter, gram, layer); } else if (gram.type === 'string') { emitter.emit(`"${gram.value}"`); } else if (gram.type === 'null') { emitter.emit('nullptr'); } else if (gram.type === 'behavior' || gram.type === 'call' || gram.type === 'var' || gram.type === 'instance') { this.grammer(emitter, gram.value, false, false); } else if (gram.type === 'number' || gram.type === 'param' || gram.type === 'bool') { emitter.emit(gram.value); } else if (gram.type === 'expr') { if (Array.isArray(gram.value)) { gram.value.forEach(gramItem => { this.grammer(emitter, gramItem, false, false); }); } else { this.grammer(emitter, gram.value, false, false); } } else if (gram.type === 'array') { let itemType = this.emitType(gram.dataType.itemType); if (gram.value.length) { emitter.emitln(`vector<${itemType}>({`); this.levelUp(); gram.value.forEach((item, i) => { if (item instanceof AnnotationItem) { this.emitAnnotation(emitter, item); return; } emitter.emit('', this.level); this.grammerValue(emitter, item, false, false); if (i < gram.value.length - 1) { emitter.emitln(','); } else { emitter.emitln(); } }); this.levelDown(); emitter.emit('})', this.level); } else { emitter.emit(`vector<${itemType}>()`); } } else if (gram.type === 'not') { emitter.emit(_symbol(Symbol.reverse())); this.grammerValue(emitter, gram.value); } else { debug.stack(gram); } } grammerCall(emitter, gram) { if (gram.type === 'sys_func' || gram.type === 'method') { const resolve_method = _resolveGrammerCall(gram, this.dependencies); if (resolve_method !== null) { if (!modules[resolve_method]) { debug.stack(`Unsupported method : ${resolve_method}`); } modules[resolve_method].call(this, emitter, gram); return; } let params = gram.params.length > 0 ? this.resolveParams(gram) : ''; emitter.emit(this.resolveCallPath(gram.path, params, gram)); } else if (gram.type === 'prop') { emitter.emit(this.resolveCallPath(gram.path, '', gram)); } else if (gram.type === 'key') { emitter.emit(this.resolveCallPath(gram.path, '', gram)); } else { debug.stack(gram); } } grammerExpr(emitter, gram) { if (gram.opt === Symbol.concat()) { // implement by behaviorTamplateString debug.stack(gram); } this.exprIsAssignToPtr = this.isPointerVar(gram.left); if (!gram.left && !gram.right) { // only emit symbol emitter.emit(` ${_symbol(gram.opt)} `); this.exprIsAssignToPtr = null; return; } // emit left of expr if (gram.opt !== Symbol.assign() && this.exprIsAssignToPtr) { emitter.emit('*'); } this.grammer(emitter, gram.left, false, false); if (gram.right.type === 'null') { // not emit symbol&right if right of expr is null this.exprIsAssignToPtr = null; if (gram.left instanceof GrammerVar) { return; } } // emit right of expr if (gram.opt === Symbol.assign()) { let isNewObject = false; let right = null; if (gram.right instanceof GrammerValue && gram.right.type === 'instance' && gram.right.value instanceof GrammerNewObject) { isNewObject = true; right = gram.right.value; } else if (gram.right instanceof GrammerNewObject) { isNewObject = true; right = gram.right; } if (isNewObject) { this.exprIsAssignToPtr = this.isPointerVar(gram.left); // is new object if (this.isPointerVar(gram.left)) { // new object to ptr property emitter.emit(` ${_symbol(gram.opt)} `); // emit symbol of expr this.grammerNewObject(emitter, right, true, true); this.exprIsAssignToPtr = null; return; } this.grammerNewObject(emitter, right, false); this.exprIsAssignToPtr = null; return; } emitter.emit(` ${_symbol(gram.opt)} `); // emit symbol of expr let toStream = false; let leftIsPointer; let leftDataType = this.resolveDataType(gram.left); if (leftDataType !== 'Darabonba::Stream' && gram.left instanceof GrammerValue && gram.left.type === 'call' && gram.left.value.path[1].name === 'body') { const k = gram.left.value.path.map(item => item.name).join('.'); toStream = k === '__request.body' && is.string(gram.right.dataType); leftIsPointer = true; } else { toStream = leftDataType === 'Darabonba::Stream'; leftIsPointer = this.isPointerVar(gram.left); } let rightIsPointer = this.isPointerVar(gram.right); if (toStream) { emitter.emit(`${this.config.tea.converter.name}::toStream(`); } let dataType = this.resolveDataType(gram.right); if (dataType === null) { dataType = this.resolveDataType(gram.left); } if (leftIsPointer && rightIsPointer) { if (toStream) { emitter.emit('*'); } this.exprIsAssignToPtr = true; this.grammer(emitter, gram.right, false, false); } else if (leftIsPointer && !rightIsPointer) { this.exprIsAssignToPtr = true; if (toStream) { this.grammer(emitter, gram.right, false, false); } else { emitter.emit(this.emitMakeShared(dataType, gram.right)); } } else if (!leftIsPointer && rightIsPointer) { emitter.emit('*'); this.grammer(emitter, gram.right, false, false); } else { this.grammer(emitter, gram.right, false, false); } if (toStream) { emitter.emit(')'); } this.exprIsAssignToPtr = null; return; } emitter.emit(` ${_symbol(gram.opt)} `); // emit symbol of expr this.grammer(emitter, gram.right, false, false); this.exprIsAssignToPtr = null; } grammerLoop(emitter, gram) { this.pushInclude('algorithm'); if (gram.type === 'foreach') { emitter.emit('for(auto '); this.grammerVar(emitter, gram.item, false, false); emitter.emit(' : '); if (this.isPointerVar(gram.source)) { emitter.emit('*'); } this.grammer(emitter, gram.source, false, false); emitter.emitln(') {'); } this.levelUp(); gram.body.forEach(node => { this.grammer(emitter, node); }); this.levelDown(); emitter.emitln('}', this.level); } grammerBreak(emitter, gram) { emitter.emit('break'); } grammerCondition(emitter, gram) { if (gram.type === 'elseif') { emitter.emit('else if'); } else { emitter.emit(`${gram.type}`); } if (gram.type !== 'else') { emitter.emit(' ('); let emit = new Emitter(this.config); gram.conditionBody.forEach(condition => { this.grammer(emitter, condition, false, false); }); emitter.emit(`${emit.output})`); } emitter.emitln(' {'); this.levelUp(); gram.body.forEach(node => { this.grammer(emitter, node); }); this.levelDown(); emitter.emitln('}', this.level); if (gram.elseItem.length && gram.elseItem.length > 0) { gram.elseItem.forEach(e => { this.grammer(emitter, e); }); } } grammerTryCatch(emitter, gram) { emitter.emitln('try {'); this.levelUp(); gram.body.forEach(node => { this.grammer(emitter, node); }); this.levelDown(); emitter.emitln('}', this.level); gram.catchBody.forEach(node => { assert.strictEqual(true, node instanceof GrammerCatch); this.grammerCatch(emitter, node); }); if (gram.finallyBody) { this.grammerFinally(emitter, gram.finallyBody); } } grammerCatch(emitter, gram) { let emitterVar = new Emitter(this.config); this.grammerVar(emitterVar, gram.exceptions.exceptionVar, false); let varName = emitterVar.output; emitter.emit(`catch (${this.emitType(gram.exceptions.type)} &`, this.level); this.addStatement(varName, gram.exceptions.type, false); emitter.emit(varName); emitter.emitln(') {'); this.levelUp(); gram.body.forEach(childGram => { this.grammer(emitter, childGram); }); this.levelDown(); emitter.emitln('}', this.level); } grammerFinally(emitter, gram) { emitter.emitln('catch(std::exception &e) {', this.level); this.levelUp(); gram.body.forEach(childGram => { this.grammer(emitter, childGram); }); this.levelDown(); emitter.emitln('}', this.level); } grammerContinue(emitter, gram) { emitter.emit('continue'); } grammerThrows(emitter, gram) { this.pushInclude('throw_exception'); emitter.emit('BOOST_THROW_EXCEPTION('); // emit exception_begin if (gram.exception) { let isException = is.object(gram.exception) && gram.exception.objectName === '$Exception'; if (isException) { emitter.emit('std::runtime_error('); } else { emitter.emit(`${this.emitType(gram.exception)}(`); } } // emit exception_body if (!gram.params.length) { let msg = gram.message ? `"${gram.message}"` : ''; emitter.emit(msg); } else if (gram.params.length === 1) { let isError = is.object(gram.exception) && gram.exception.objectName === '$Error'; if (isError) { let dataType = this.resolveDataType(gram.params[0]); emitter.emit(`${dataType}(`); this.grammerValue(emitter, gram.params[0]); emitter.emit(')'); } else { this.grammerValue(emitter, gram.params[0]); } } else { let tmp = []; gram.params.forEach(p => { let emit = new Emitter(this.config); this.grammerValue(emit, p); tmp.push(emit.output); }); emitter.emit(tmp.join(', ')); } // emit exception_end if (gram.exception) { emitter.emit(')'); } emitter.emit(')'); // close BOOST_THROW_EXCEPTION } grammerNewObject(emitter, gram, isAssign = true, isPtrParam = false) { let objectName = gram.name; if (!this.exprIsAssignToPtr && isAssign) { emitter.emit(`${this.resolveName(objectName)}(`); } else if (isAssign) { emitter.emit(`make_shared<${this.resolveName(objectName)}>(`); } let tmp = []; let params; if (Array.isArray(gram.params)) { params = gram.params; } else { params = [gram.params]; } if (params.length) { params.forEach(p => { if (p instanceof GrammerValue && p.type === 'model_construct_params' && !p.value.length) { return; } else if (p instanceof GrammerValue && p.type === 'model_construct_params') { isPtrParam = false; } let emit = new Emitter(this.config); if (isPtrParam && !this.isPointerVar(p)) { emit.emit(this.emitMakeShared(p.dataType ? p.dataType : p.type, p)); } else { this.grammerValue(emit, p, false, false); } tmp.push(emit.output); }); // emitter.emit(tmp.join(', ')); } if (isAssign) { emitter.emit(tmp.join(', ')); emitter.emit(')'); } else if (tmp.length) { emitter.emit('('); emitter.emit(tmp.join(', ')); emitter.emit(')'); } } grammerReturn(emitter, gram) { if (is.void(this.funcReturnType)) { emitter.emit('return'); return; } emitter.emit('return '); if (gram.type === 'null') { this.grammerValue(emitter, new GrammerValue('null'), false, false); } else if (gram.type === 'grammer') { let returnPointer = this.isPointerType(this.funcReturnType); if (this.isPointerVar(gram.expr) && !returnPointer) { emitter.emit('*'); } this.grammer(emitter, gram.expr, false, false); } else if (gram.type === 'string') { emitter.emit('string("")'); } else { this.grammer(emitter, gram.expr, false, false); } } /**************************************** behavior ****************************************/ behaviorTimeNow(emitter, behavior) { emitter.emit('0'); } behaviorDoAction(emitter, behavior) { emitter.emit('', this.level); this.grammerVar(emitter, behavior.var); emitter.emit(` = make_shared<${this.addInclude('$Response')}>(${this.addInclude('$Core')}::${this.config.tea.core.doAction}(`); let params = []; behavior.params.forEach(p => { let emit = new Emitter(this.config); if (this.isPointerVar(p)) { emit.emit('*'); } this.grammerValue(emit, p); params.push(emit.output); }); emitter.emit(params.join(', ')); emitter.emitln('));'); behavior.body.forEach(node => { this.grammer(emitter, node); }); } behaviorToMap(emitter, behavior) { const grammer = behavior.grammer; if (grammer instanceof GrammerCall) { grammer.path.push({ type: 'call', name: 'toMap' }); this.grammerCall(emitter, grammer); } else if (grammer instanceof GrammerVar) { const grammerCall = new GrammerCall('method'); grammerCall.path.push({ type: 'object', name: grammer.name }); grammerCall.path.push({ type: 'call', name: 'toMap' }); this.grammerCall(emitter, grammerCall); } else { debug.stack(grammer); } } behaviorToModel(emitter, behavior) { emitter.emit(`${behavior.expected}(`); let type = this.resolveDataType(behavior.grammer); if (type === 'map<string, string>') { emitter.emit(`${this.config.tea.converter.name}::toGenericMap(`); this.grammer(emitter, behavior.grammer, false, false); emitter.emit(')'); } else { behavior.grammer.type = 'model_construct_params'; this.grammer(emitter, behavior.grammer, false, false); } emitter.emit(')'); } behaviorSetMapItem(emitter, behavior) { let emit = new Emitter(this.config); this.grammerCall(emit, behavior.call); this.pushInclude('map'); let paths = behavior.call.path; let key = super.resolveName(behavior.key); if (this.isPointerPath(paths.length - 1, paths)) { emitter.emit(`${emit.output}->insert(pair<string, ${this.emitType(behavior.value.dataType)}>("${key}", `, this.level); } else { emitter.emit(`${emit.output}.insert(pair<string, ${this.emitType(behavior.value.dataType)}>("${key}", `, this.level); } if (this.isPointerVar(behavior.value)) { emitter.emit('*'); } this.grammerValue(emitter, behavior.value); emitter.emitln('));'); } behaviorRetry(emitter, behavior) { emitter.emitln(`throw ${this.addInclude('$Error')}(${this.config.request}, ${this.config.response});`, this.level); } behaviorTamplateString(emitter, behavior) { let tmp = []; behavior.items.forEach(item => { let emit = new Emitter(this.config); if (this.isPointerVar(item) && !is.any(item.dataType)) { emit.emit('*'); } if (is.string(item.dataType)) { this.grammer(emit, item, false, false); } else if (is.any(item.dataType)) { this.pushInclude('darabonba_core'); emit.emit(`${this.config.tea.converter.name}::toString(`); this.grammer(emit, item, false, false); emit.emit(')'); } else { this.pushInclude('cast'); emit.emit('boost::lexical_cast<string>('); this.grammer(emit, item, false, false); emit.emit(')'); } tmp.push(emit.output); }); emitter.emit(`${tmp.filter(s => s !== '""').map(s => `string(${s})`).join(' + ')}`); } } module.exports = Combinator;