lib/generator.js (3,780 lines of code) (raw):
'use strict';
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const REQUEST = 'request_';
const RESPONSE = 'response_';
const CORE = 'Base';
const DSL = require('@darabonba/parser');
const Annotation = require('@darabonba/annotation-parser');
const { Tag } = DSL.Tag;
const getBuiltin = require('./builtin');
const {
_name, _format, _string, _type, _initValue, _avoidReserveName, _importFilter, _avoidVariableKeywords,
_setExtendFunc, _isFilterType, _getAttr, _setValueFunc, _vid, _pointerType, _lowerFirst, _escape,
_isBinaryOp, _upperFirst, _isIterator, _modelName, _deleteWithSuffix, _snakeCase
} = require('./helper');
function getAttr(node, attrName) {
for (let i = 0; i < node.attrs.length; i++) {
if (_name(node.attrs[i].attrName) === attrName) {
return node.attrs[i].attrValue.string || node.attrs[i].attrValue.lexeme || node.attrs[i].attrValue.value;
}
}
}
class Visitor {
constructor(option = {}) {
this.baseClient = option && option.baseClient;
this.typedef = option.go && option.go.typedef ? option.go.typedef : {};
this.importsTypedef = {};
const pack = option && option.package || [];
this.uselessPack = [];
Object.keys(this.typedef).forEach((def) => {
if (this.typedef[def].import && this.typedef[def].type) {
if (!pack.includes(this.typedef[def].import)) {
pack.push(this.typedef[def].import);
this.uselessPack.push(this.typedef[def].import);
}
}
});
this.goPackages = pack.map(packageName => {
if (packageName.includes(' ')) {
return ` ${packageName}`;
}
if (packageName !== 'io') {
return ` "${packageName}"`;
}
});
this.config = {
outputDir: '',
indent: ' ',
clientPath: '/client/client.go',
...option
};
this.output = '';
this.outputDir = option.outputDir;
this.__module = {};
this.exec = this.config.exec;
this.editable = this.config.editable;
this.noCompatible = this.config.noCompatible;
if (this.exec) {
this.config.clientPath = '/main/main.go';
}
if (!this.outputDir) {
throw new Error('`option.outputDir` should not empty');
}
if (!fs.existsSync(this.outputDir)) {
fs.mkdirSync(this.outputDir, {
recursive: true
});
}
}
save(filepath) {
const targetPath = path.join(this.outputDir, filepath);
fs.mkdirSync(path.dirname(targetPath), {
recursive: true
});
const modPath = path.join(this.outputDir, 'go.mod');
if (!fs.existsSync(modPath)) {
const modFile = this.generatorMod(this.module);
fs.writeFileSync(modPath, modFile);
} else {
const original = fs.readFileSync(modPath, 'UTF-8');
const modFile = this.reWriteMod(this.module, original);
fs.writeFileSync(modPath, modFile);
}
fs.writeFileSync(targetPath, this.header + this.output);
this.output = '';
}
reWriteMod(module, original) {
let content = `require (\n`;
const originMods = {};
const regex = /require\s*\(\s*([\s\S]*?)\s*\)/gm;
const match = regex.exec(original);
if (match) {
const requireBlock = match[1].trim();
const packageRegex = /^\s*([^()\s]+)\s+([^\s()]+)$/gm;
let packageMatch;
while ((packageMatch = packageRegex.exec(requireBlock)) !== null) {
const path = packageMatch[1];
const version = packageMatch[2];
originMods[path] = version;
}
}
const mods = [];
Object.keys(module.importPackages).forEach((aliasId) => {
const importPackage = module.importPackages[aliasId];
if(!importPackage.version || !importPackage.path) {
return;
}
if(originMods[importPackage.path]) {
originMods[importPackage.path] = importPackage.version;
return;
}
mods.push(' ' + importPackage.path + ' ' + importPackage.version);
});
Object.keys(originMods).forEach((path) => {
mods.push(' ' + path + ' ' + originMods[path]);
});
content += [...new Set(mods)].join('\n') + `\n)`;
return original.replace(regex, content);
}
generatorMod(module) {
var content = `module ${module.moduleName}\n`;
content += `\n`;
content += `require (\n`;
const mods = [];
Object.keys(module.importPackages).forEach((aliasId) => {
const importPackage = module.importPackages[aliasId];
if(!importPackage.version || !importPackage.path) {
return;
}
mods.push(' ' + importPackage.path + ' ' + importPackage.version);
});
content += [...new Set(mods)].join('\n') + `\n)\n`;
return content;
}
getPkgName(filepath) {
if(filepath.startsWith(this.outputDir)) {
return filepath.replace(this.outputDir, `${this.module.moduleName}/`);
} else if(filepath.startsWith('./')) {
return filepath.replace('./', `${this.module.moduleName}/`);
} else if(filepath.startsWith('../')) {
return filepath.replace('../', `${this.module.moduleName}/`);
}
throw Error(`${filepath} is not valid path.`);
}
emit(str, level) {
this.output += ' '.repeat(level * 2) + str;
}
visit(ast, level = 0) {
const filepath = this.exec ? 'main/main.go' : 'client/client.go';
this.ast = ast;
this.visitModule(ast, filepath, true, level);
}
overwrite(ast, filepath) {
if(!ast.moduleBody.nodes || !ast.moduleBody.nodes.length) {
return;
}
const beginNotes = DSL.note.getNotes(this.notes, 0, ast.moduleBody.nodes[0].tokenRange[0]);
const overwirte = beginNotes.find(note => note.note.lexeme === '@overwrite');
const targetPath = path.join(this.outputDir, filepath);
if(overwirte && overwirte.arg.value === false && fs.existsSync(targetPath)) {
return false;
}
return true;
}
clientNameDefine(aliasId, pointer = true) {
const self = this;
Object.defineProperty(this.clientName, aliasId, {
get() {
let prefix = pointer ? '*' : '';
const { pkgName } = self.module.importPackages[aliasId];
self.imports.push({
aliasId,
pkgName,
});
return prefix + _importFilter(_format(aliasId).toLowerCase()) + '.' + self.constructFunc[aliasId];
},
enumerable: true,
configurable: false
});
}
visitImports(imports, usedTypes, innerModule) {
const teafilePath = fs.existsSync(path.join(this.config.pkgDir, 'Teafile')) ?
path.join(this.config.pkgDir, 'Teafile') : path.join(this.config.pkgDir, 'Darafile');
const teaFile = JSON.parse(fs.readFileSync(teafilePath, 'utf8'));
const release = teaFile.releases && teaFile.releases.go || '';
const strs = release.split(':');
this.module = {
importPackages: {},
moduleName: strs[0].substring(0, strs[0].lastIndexOf('/')) || 'client'
};
if(!this.structName) {
this.structName = this.config.go ? this.config.go.clientName || 'Client' : 'Client';
}
this.clientName = {};
this.constructFunc = {};
if (imports.length > 0) {
const lockPath = path.join(this.config.pkgDir, '.libraries.json');
const lock = fs.existsSync(lockPath) ? JSON.parse(fs.readFileSync(lockPath, 'utf8')) : {};
for (let i = 0; i < imports.length; i++) {
const item = imports[i];
const aliasId = _name(item);
const main = item.mainModule;
const inner = item.module;
const moduleDir = main ? this.config.libraries[main] : this.config.libraries[aliasId];
const innerPath = item.innerPath;
if (innerPath) {
const filepath = innerPath.replace(/(\.tea)$|(\.spec)$|(\.dara)$/gi, '');
const pkgName = this.getPkgName(filepath);
innerModule.set(aliasId, filepath);
this.module.importPackages[aliasId] = {
pkgName: pkgName,
name: aliasId,
inner: true
};
this.constructFunc[aliasId] = this.getInnerClient(aliasId);
this.clientNameDefine(aliasId);
continue;
}
let targetPath = '';
if (moduleDir.startsWith('./') || moduleDir.startsWith('../')) {
targetPath = path.join(this.config.pkgDir, moduleDir);
} else if (moduleDir.startsWith('/')) {
targetPath = moduleDir;
} else {
targetPath = path.join(this.config.pkgDir, lock[moduleDir]);
}
const pkgPath = fs.existsSync(path.join(targetPath, 'Teafile')) ? path.join(targetPath, 'Teafile') : path.join(targetPath, 'Darafile');
const importTeaFile = JSON.parse(fs.readFileSync(pkgPath));
const goPkg = importTeaFile.releases && importTeaFile.releases.go;
if (importTeaFile.go && importTeaFile.go.typedef) {
this.importsTypedef[aliasId] = {};
const moduleTypedef = importTeaFile.go && importTeaFile.go.typedef;
Object.keys(moduleTypedef).forEach((types) => {
if (!this.importsTypedef[aliasId][types]) {
this.importsTypedef[aliasId][types] = {};
}
this.importsTypedef[aliasId][types].import = moduleTypedef[types].import;
this.importsTypedef[aliasId][types].type = moduleTypedef[types].type;
});
}
if (importTeaFile.go && importTeaFile.go.clientName) {
if (importTeaFile.go.clientName.indexOf('*') === 0) {
this.constructFunc[aliasId] = importTeaFile.go.clientName.substring(1);
this.clientNameDefine(aliasId);
} else {
this.constructFunc[aliasId] = importTeaFile.go.clientName;
this.clientNameDefine(aliasId, false);
}
} else {
this.constructFunc[aliasId] = 'Client';
this.clientNameDefine(aliasId);
}
if (!goPkg) {
throw new Error(`The '${aliasId}' has no Go supported.`);
}
const [pkgName, version] = goPkg.split(':');
this.module.importPackages[aliasId] = {
path: pkgName.substring(0, pkgName.lastIndexOf('/')),
pkgName: pkgName,
version: version,
name: aliasId,
goPkg: importTeaFile.go
};
if(inner) {
const mainPath = pkgName.substring(0, pkgName.lastIndexOf('/'));
const innerPath = importTeaFile.exports[inner].replace(/(\.tea)$|(\.spec)$|(\.dara)$/gi, '');
const pkgPath = path.join(mainPath, innerPath);
this.module.importPackages[aliasId] = {
pkgName: pkgPath,
name: aliasId,
inner: true,
};
}
}
}
}
getInnerClient(aliasId) {
const moduleAst = this.ast.innerDep.get(aliasId);
const beginNotes = DSL.note.getNotes(moduleAst.notes, 0, moduleAst.moduleBody.nodes[0].tokenRange[0]);
const clientNote = beginNotes.find(note => note.note.lexeme === '@clientName');
if(clientNote) {
return _string(clientNote.arg.value);
}
return 'Client';
}
saveInnerModule(ast) {
const keys = ast.innerModule.keys();
let data = keys.next();
while (!data.done) {
const aliasId = data.value;
const moduleAst = ast.innerDep.get(aliasId);
this.structName = this.constructFunc[aliasId];
const filepath = path.join(ast.innerModule.get(aliasId), 'client.go');
this.visitModule(moduleAst, filepath, false, 0);
data = keys.next();
}
}
visitInterface(ast, apis, level) {
// 被引用时需要使用interface实现多态
if (this.config.go && this.config.go.interface) {
let interfaceName = this.config.go.clientName || 'Client';
this.emit(`type ${interfaceName}Interface interface {\n`, level);
let NonStaticFuncs = ast.moduleBody.nodes.filter(item => {
return item.type === 'function' && !item.isStatic;
});
for (let func of NonStaticFuncs) {
const env = {
apis,
local: new Map(),
returnType: func.returnType,
hasThrow: func.isAsync || func.hasThrow,
nestFuncParamName: new Map(),
yieldFunc: _isIterator(func.returnType),
nestFuncParamNameSubscript: { 'count': 0 }
};
this.emit(`${_format(_name(func.functionName))} `, level + 1);
this.visitParams(func.params, level, env);
this.visitReturnType(func, level, env, false);
}
this.emit(`}\n\n`, level);
}
}
visitModuleClient(ast, apis, nonStaticFuncs, level) {
if (!ast.extends && (apis.length > 0 || nonStaticFuncs.length > 0)) {
this.emit(`type ${this.structName} struct {\n`, level);
if(!this.noCompatible) {
this.emit(`DisableSDKError *bool\n`, level + 1);
}
for (let i = 0; i < ast.moduleBody.nodes.length; i++) {
const node = ast.moduleBody.nodes[i];
if (node.type === 'type' && node.vid) {
let comments = DSL.comment.getFrontComments(this.comments, node.tokenRange[0]);
this.visitComments(comments, level + 1);
this.emit(`${_format(_name(node.vid).substring(1))} `, level + 1);
this.visitPointerType(node.value, level);
this.emit(`\n`);
}
}
this.emit(`}\n\n`, level);
} else if (ast.extends) {
this.emit(`type ${this.structName} struct {\n`, level);
this.emit('', level + 1);
this.visitModuleName(ast.extends);
this.emit(`.${this.constructFunc[_name(ast.extends)]}\n`);
if(!this.noCompatible) {
this.emit(`DisableSDKError *bool\n`, level + 1);
}
for (let i = 0; i < ast.moduleBody.nodes.length; i++) {
const node = ast.moduleBody.nodes[i];
if (node.type === 'type' && node.vid) {
let comments = DSL.comment.getFrontComments(this.comments, node.tokenRange[0]);
this.visitComments(comments, level + 1);
this.emit(`${_avoidVariableKeywords(_format(_name(node.vid).substring(1)))} `, level + 1);
this.visitPointerType(node.value, level);
this.emit(`\n`);
}
}
this.emit(`}\n\n`, level);
}
}
saveBuffer(name, type) {
const filename = `${_snakeCase(name)}_${type}.go`;
if(this.fileBuffer[filename]) {
this.fileBuffer[filename].imports = this.imports.concat(this.fileBuffer[filename].imports);
this.fileBuffer[filename].builtinModule = this.builtinModule.concat(this.fileBuffer[filename].builtinModule);
this.fileBuffer[filename].output = this.output + this.fileBuffer[filename].output;
return;
}
this.fileBuffer[filename] = {
imports: this.imports,
output: this.output,
builtinModule: this.builtinModule,
};
}
flushBuffer(packageName, filepath) {
Object.keys(this.fileBuffer).map(filename => {
this.output = this.fileBuffer[filename].output;
this.imports = this.fileBuffer[filename].imports;
this.builtinModule = this.fileBuffer[filename].builtinModule;
filepath = path.join(path.dirname(filepath), filename);
this.visitHeader(this.__module, packageName);
this.save(filepath);
this.output = '';
this.imports = [];
this.builtinModule = [];
});
}
visitModule(ast, filepath, main, level) {
assert.equal(ast.type, 'module');
this.notes = ast.notes;
this.fileBuffer = {};
if(this.overwrite(ast, filepath) === false) {
return;
}
const apis = ast.moduleBody.nodes.filter((item) => {
return item.type === 'api';
});
const models = ast.moduleBody.nodes.filter((item) => {
return item.type === 'model';
});
const exceptions = ast.moduleBody.nodes.filter((item) => {
return item.type === 'exception';
});
const nonStaticFuncs = ast.moduleBody.nodes.filter((item) => {
return item.type === 'function' && !item.isStatic;
});
const initParts = ast.moduleBody.nodes.filter((item) => {
return item.type === 'init';
});
ast.innerModule = new Map();
this.comments = ast.comments;
this.predefined = ast.models;
this.usedExternException = ast.usedExternException;
this.builtin = getBuiltin(this);
this.builtinModule = [];
this.tryFunc = [];
this.yieldFunc = [];
this.imports = [];
this.visitAnnotation(ast.annotation, level);
this.importPackages = '';
this.visitImports(ast.imports, ast.usedTypes, ast.innerModule);
const __module = this.__module;
const packageName = path.dirname(filepath).split('/').pop();
// global definition
this.modelBefore(filepath, level);
for (let i = 0; i < models.length; i++) {
this.visitModelInterface(models[i], level);
this.visitModel(models[i], level);
this.saveBuffer(_format(_name(models[i].modelName)), 'model');
this.output = '';
this.imports = [];
this.builtinModule = [];
}
for (let i = 0; i < exceptions.length; i++) {
this.visitExceptionInterface(exceptions[i], level);
this.visitException(exceptions[i], level);
this.saveBuffer(_upperFirst(_name(exceptions[i].exceptionName)), 'error');
this.output = '';
this.imports = [];
this.builtinModule = [];
}
this.modelAfter(__module, level);
this.flushBuffer(packageName, filepath);
this.moduleBefore(ast, main, level);
this.visitInterface(ast, apis, level);
this.visitModuleClient(ast, apis, nonStaticFuncs, level);
// models definition
if (initParts.length > 0 && (apis.length > 0 || nonStaticFuncs.length > 0)) {
this.visitInit(initParts[0], level);
this.apiBefore(__module, level);
} else if (ast.extends) {
this.emit(`func NewClient(config *${_name(ast.extends).toLowerCase()}.Config)(*${this.structName}, error) {\n`, level);
this.emit(`client := new(${this.structName})\n`, level + 1);
this.emit(`err := client.Init(config)\n`, level + 1);
this.emit(`return client, err\n`, level + 1);
this.emit(`}\n\n`, level);
}
// else {
// this.emit(`func NewClient() error {\n`, level);
// this.emit(`err := dara.NewSDKError(map[string]interface{}{\n`, level + 1);
// this.emit(`"message": "Un-Support!"\n`, level + 2);
// this.emit('})\n', level + 1);
// this.emit(`return err\n`, level + 1);
// this.emit(`}\n\n`, level);
// }
for (let i = 0; i < apis.length; i++) {
this.eachAPI(apis[i], level, ast.predefined);
}
if (apis.length > 0) {
this.apiAfter(__module, level);
}
this.wrapBefore(__module, level);
const funcs = ast.moduleBody.nodes.filter((item) => {
return item.type === 'function';
});
for (let i = 0; i < funcs.length; i++) {
this.eachFunc(funcs[i], level, ast.predefined, apis);
}
for (let i = 0; i < this.tryFunc.length; i++) {
this.eachTryFunc(this.tryFunc[i], level);
}
for (let i = 0; i < this.yieldFunc.length; i++) {
this.eachYieldFunc(this.yieldFunc[i], level);
}
if (this.exec) {
this.emit(`\nfunc main() {\n`, 0);
this.emit(`err := _main(dara.StringSlice(os.Args[1:]))\n`, 1);
this.emit(`if err != nil {\n`, 1);
this.emit('panic(err)\n', 2);
this.emit(`}\n`, 1);
this.emit('}\n', 0);
}
this.wrapAfter(filepath, level);
this.visitHeader(this.__module, packageName, level);
this.moduleAfter(__module, level);
this.save(filepath);
this.mainModule = false;
this.saveInnerModule(ast);
}
visitConstructor(){
}
visitAnnotation(annotation, level) {
if (!annotation || !annotation.value) {
return;
}
let comments = DSL.comment.getFrontComments(this.comments, annotation.index);
this.visitComments(comments, level);
var ast = Annotation.parse(annotation.value);
var description = ast.items.find((item) => {
return item.type === 'description';
});
var summary = ast.items.find((item) => {
return item.type === 'summary';
});
var _return = ast.items.find((item) => {
return item.type === 'return';
});
var deprecated = ast.items.find((item) => {
return item.type === 'deprecated';
});
var params = ast.items.filter((item) => {
return item.type === 'param';
}).map((item) => {
return {
name: item.name.id,
text: item.text.text
};
});
var throws = ast.items.filter((item) => {
return item.type === 'throws';
}).map((item) => {
return item.text.text;
});
let hasNextSection = false;
const summaryText = summary ? _escape(summary.text.text).trimEnd() : '';
const descriptionText = description ? _escape(description.text.text).trimEnd() : '';
const returnText = _return ? _escape(_return.text.text).trimEnd() : '';
if (deprecated) {
const deprecatedText = _escape(deprecated.text.text).trimEnd();
deprecatedText.split('\n').forEach((line, index, array) => {
if(index === 0) {
this.emit(`// Deprecated: ${line}\n`, level);
if (array.length > 1) {
this.emit(`//\n`, level);
}
} else {
this.emit(`// ${line}\n`, level);
if (index < array.length - 1){
this.emit(`//\n`, level);
}
}
});
hasNextSection = true;
}
if (summaryText !== '') {
if (hasNextSection) {
this.emit(`// \n`, level);
}
this.emit(`// Summary:\n`, level);
this.emit(`// \n`, level);
summaryText.split('\n').forEach((line, index, array) => {
this.emit(`// ${line}\n`, level);
if (index < array.length - 1) {
this.emit(`// \n`, level);
}
});
hasNextSection = true;
}
if (descriptionText !== '') {
if (hasNextSection) {
this.emit(`// \n`, level);
}
this.emit(`// Description:\n`, level);
this.emit(`// \n`, level);
descriptionText.split('\n').forEach((line, index, array) => {
this.emit(`// ${line}\n`, level);
if (index < array.length - 1) {
this.emit(`// \n`, level);
}
});
hasNextSection = true;
}
if (params.length > 0) {
if (hasNextSection) {
this.emit(`// \n`, level);
}
params.forEach((item, i) => {
this.emit(`// @param ${item.name} - `, level);
const items = item.text.trimEnd().split('\n');
items.forEach((line, j) => {
if (j === 0) {
this.emit(`${line}\n`);
} else {
this.emit(`// ${line}\n`, level);
}
if (j < items.length - 1 || (j === items.length - 1 && i < params.length -1)) {
this.emit(`// \n`, level);
}
});
});
hasNextSection = true;
}
if (returnText) {
if (hasNextSection) {
this.emit(`// \n`, level);
}
this.emit(`// @return `, level);
const returns = returnText.split('\n');
returns.forEach((line, index) => {
if (index === 0) {
this.emit(`${line}\n`);
} else {
this.emit(`// ${line}\n`, level);
}
if (index < returns.length - 1) {
this.emit(`// \n`, level);
}
});
hasNextSection = true;
}
if (throws.length > 0) {
if (hasNextSection) {
this.emit(`// \n`, level);
}
throws.forEach((item, i) => {
this.emit(`// @throws `, level);
const items = item.trimEnd().split('\n');
items.forEach((line, j) => {
if (j === 0) {
this.emit(`${line}\n`);
} else {
this.emit(`// ${line}\n`, level);
}
if (j < items.length - 1 || (j === items.length - 1 && i < params.length -1)) {
this.emit(`// \n`, level);
}
});
});
}
}
visitComments(comments, level) {
comments.forEach(comment => {
this.emit(`${comment.value}`, level);
this.emit(`\n`);
});
}
visitParams(ast, level, env) {
assert.equal(ast.type, 'params');
env.pointerParams = [];
this.emit('(');
for (var i = 0; i < ast.params.length; i++) {
if (i !== 0) {
this.emit(', ');
}
const node = ast.params[i];
assert.equal(node.type, 'param');
const name = _name(node.paramName);
env.pointerParams.push(_avoidReserveName(name));
this.emit(`${_avoidReserveName(name)}`);
if (node.paramType) {
const paramType = node.paramType;
this.emit(` `);
this.visitPointerType(paramType, level, env);
}
}
if(env.yieldFunc) {
if(ast.params.length > 0) {
this.emit(', ');
}
this.emit('_yield chan ');
this.visitPointerType(env.returnType, level, env);
if(env.hasThrow) {
this.emit(', _yieldErr chan error');
}
}
this.emit(')');
}
visitInit(ast, level) {
const env = {
local: new Map(),
funcName: 'NewClient'
};
assert.equal(ast.type, 'init');
this.visitAnnotation(ast.annotation, level);
let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]);
this.visitComments(comments, level);
this.emit(`func NewClient`, level);
this.visitParams(ast.params, level, env);
this.emit(`(*${this.structName}, error) {\n`);
this.emit(`client := new(${this.structName})\n`, level + 1);
this.emit(`err := client.Init(`, level + 1);
for (let i = 0; i < ast.params.params.length; i++) {
const param = ast.params.params[i];
this.emit(`${_name(param.paramName)}`);
if (i < ast.params.params.length - 1) {
this.emit(`, `);
}
}
this.emit(`)\n`);
this.emit(`return client, err\n`, level + 1);
this.emit(`}\n\n`, level);
this.emit(`func (client *${this.structName})Init`, level);
this.visitParams(ast.params, level, env);
this.emit(`(_err error) {\n`);
if (ast.initBody && ast.initBody.stmts && ast.initBody.stmts.length > 0) {
this.visitStmts(ast.initBody, level + 1, env);
}
this.emit(`return nil\n`, level + 1);
this.emit(`}\n\n`, level);
}
visitFunctionNested(ast, level, env) {
var argHasThrowFunc = new Map;
for (let i = 0; i < ast.args.length; i++) {
if (ast.args[i].type === 'call' && ast.args[i].hasThrow) {
let paramName;
if (ast.args[i].left.type === 'method_call') {
paramName = _lowerFirst(_name(ast.args[i].left.id)) + 'Tmp';
} else {
paramName = _lowerFirst(_name(ast.left.propertyPath[0])) + 'Tmp';
}
if (env.nestFuncParamName.has(paramName)) {
let count = env.nestFuncParamNameSubscript['count'] + 1;
paramName = paramName + count;
}
env.nestFuncParamName.set(paramName);
argHasThrowFunc.set(i, paramName);
this.emit(`${paramName}, err := `, level);
this.visitExpr(ast.args[i], level, env, { pointer: true });
this.emit('\n');
this.emit(`if err != nil {\n`, level);
this.emit(`_err = err\n`, level + 1);
if (env.returnType && _name(env.returnType) !== 'void') {
this.emit(`return _result, _err\n`, level + 1);
} else {
this.emit(`return _err\n`, level + 1);
}
this.emit(`}\n`, level);
}
}
return argHasThrowFunc;
}
visitReturnType(ast, level, env, needClose = true) {
if(env.yieldFunc) {
this.emit(` ${needClose ? `{\n` : `\n`}`);
return;
}
if (_name(ast.returnType) !== 'void') {
this.emit(` (_result `);
this.visitPointerType(ast.returnType, level, env);
if (env.hasThrow) {
this.emit(`, _err error) ${needClose ? `{\n` : `\n`}`);
} else {
this.emit(`) ${needClose ? `{\n` : `\n`}`);
}
return;
}
if (env.hasThrow) {
this.emit(` (_err error) ${needClose ? `{\n` : `\n`}`);
} else {
this.emit(` ${needClose ? `{\n` : `\n`}`);
}
}
checkExpr(expr) {
if(!expr) {
return 0;
}
if(( expr.type === 'call' &&
expr.hasThrow) || expr.type === 'construct') {
return 1;
}
return 0;
}
stmtsErrCount(stmts) {
let errNum = 0;
for (var i = 0; i < stmts.length; i++) {
const ast = stmts[i];
if (ast.type === 'return') {
errNum += this.checkExpr(ast.expr);
} else if (ast.type === 'if') {
errNum += this.checkExpr(ast.condition);
errNum += this.stmtsErrCount(ast.stmts.stmts);
if(ast.elseIfs && ast.elseIfs.length > 0) {
ast.elseIfs.map(elseIf => {
errNum += this.checkExpr(elseIf.condition);
errNum += this.stmtsErrCount(elseIf.stmts.stmts);
});
}
if(ast.elseStmts) {
errNum += this.stmtsErrCount(ast.elseStmts.stmts);
}
} else if (ast.type === 'throw') {
errNum += 1;
} else if (ast.type === 'assign') {
errNum += this.checkExpr(ast.expr);
} else if (ast.type === 'declare') {
errNum += this.checkExpr(ast.expr);
} else if (ast.type === 'while') {
errNum += this.checkExpr(ast.condition);
errNum += this.stmtsErrCount(ast.stmts.stmts);
} else if (ast.type === 'for') {
errNum += this.checkExpr(ast.list);
errNum += this.stmtsErrCount(ast.stmts.stmts);
} else if (ast.type === 'try') {
errNum += 1;
} else if (ast.type === 'call' && ast.hasThrow) {
const name = _format(_name(ast.left.id));
if (!name.startsWith('$') || !this.builtin[name]) {
errNum += 1;
}
}
}
return errNum;
}
getObjectVars(ast) {
let vars = [];
const fields = ast.fields.filter((field) => {
return field.type === 'objectField';
});
const expandFields = ast.fields.filter((field) => {
return field.type === 'expandField';
});
for(let i = 0; i < fields.length; i++) {
vars = vars.concat(this.getExprVars(fields[i].expr));
}
for(let i = 0; i < expandFields.length; i++) {
vars = vars.concat(this.getExprVars(expandFields[i].expr));
}
return vars;
}
getName(ast) {
let id = _name(ast.id);
if(ast.id.tag === Tag.VID){
id = _vid(ast.id);
}
var expr = '';
if (id === '__response') {
expr += RESPONSE;
} else if (id === '__request') {
expr += REQUEST;
} else {
expr += _avoidReserveName(id);
}
return expr;
}
getModuleType(moduleType) {
if(moduleType.type === 'basic' || moduleType.type === 'model') {
return {
type: moduleType.type,
name: moduleType.name
};
}
return {
idType: 'module',
name: moduleType.name
};
}
getExprVars(ast) {
let vars = [];
if (ast.type === 'property_access') {
vars.push({
name: this.getName(ast),
type: ast.id.inferred,
});
} else if (ast.type === 'object') {
vars.concat(this.getObjectVars(ast));
} else if (ast.type === 'variable') {
vars.push({
name: this.getName(ast),
type: ast.inferred,
});
} else if (ast.type === 'virtualVariable') {
vars.push({
name: 'client',
type: this.structName,
});
} else if (ast.type === 'decrement') {
vars = vars.concat(this.getExprVars(ast.expr));
} else if (ast.type === 'increment') {
vars = vars.concat(this.getExprVars(ast.expr));
} else if (ast.type === 'template_string') {
for (let i = 0; i < ast.elements.length; i++) {
const item = ast.elements[i];
if (item.type === 'expr') {
const expr = item.expr;
vars = vars.concat(this.getExprVars(expr));
}
}
} else if (ast.type === 'call') {
for (let i = 0; i < ast.args.length; i++) {
const expr = ast.args[i];
vars = vars.concat(this.getExprVars(expr));
}
if(ast.left.type === 'instance_call') {
if (_name(ast.left.id).indexOf('@') === 0) {
vars.push({
name: 'client',
type: this.structName,
});
} else {
const type = this.getModuleType(ast.left.id.moduleType);
vars.push({
name: _name(ast.left.id),
type: type
});
}
} else if(ast.left.type === 'method_call') {
const name = _format(_name(ast.left.id));
if (name.startsWith('$') && this.builtin[name]) {
return vars;
}
if (!ast.isStatic) {
vars.push({
name: 'client',
type: this.structName,
});
}
}
} else if (ast.type === 'group') {
vars = vars.concat(this.getExprVars(ast.expr));
} else if (_isBinaryOp(ast.type)) {
vars = vars.concat(this.getExprVars(ast.left));
vars = vars.concat(this.getExprVars(ast.right));
} else if (ast.type === 'construct') {
for (let i = 0; i < ast.args.length; i++) {
vars = vars.concat(this.getExprVars(ast.args[i]));
}
} else if (ast.type === 'construct_model' && ast.object) {
vars = vars.concat(this.getObjectVars(ast.object));
} else if (ast.type === 'not') {
vars = vars.concat(this.getExprVars(ast.expr));
} else if (ast.type === 'array') {
for (let i = 0; i < ast.items.length; i++) {
vars = vars.concat(this.getExprVars(ast.items[i]));
}
} else if (ast.type === 'map_access') {
vars.push({
name: this.getName(ast),
type: {
type: 'map',
keyType: {
type: 'basic',
name: 'string'
},
valueType: ast.inferred
},
});
} else if (ast.type === 'array_access') {
vars.push({
name: this.getName(ast),
type: { type: 'array', itemType: ast.inferred },
});
} else if (ast.type === 'super') {
for (let i = 0; i < ast.args.length; i++) {
vars = vars.concat(this.getExprVars(ast.args[i]));
}
}
return vars;
}
getStmtsVars(stmts) {
let args = [];
let declare = [];
for (var i = 0; i < stmts.length; i++) {
const ast = stmts[i];
if (ast.type === 'return') {
args = args.concat(this.getExprVars(ast.expr));
} else if (ast.type === 'if') {
args = args.concat(this.getExprVars(ast.condition));
args = args.concat(this.getStmtsVars(ast.stmts.stmts));
if(ast.elseIfs && ast.elseIfs.length > 0) {
ast.elseIfs.map(elseIf => {
args = args.concat(this.getExprVars(elseIf.condition));
args = args.concat(this.getStmtsVars(elseIf.stmts.stmts));
});
}
if(ast.elseStmts) {
args = args.concat(this.getStmtsVars(ast.elseStmts.stmts));
}
} else if (ast.type === 'throw') {
args = args.concat(this.getExprVars(ast.expr));
} else if (ast.type === 'assign') {
args = args.concat(this.getExprVars(ast.left));
args = args.concat(this.getExprVars(ast.expr));
} else if (ast.type === 'declare') {
declare.push(_name(ast.id));
args = args.concat(this.getExprVars(ast.expr));
} else if (ast.type === 'while') {
args = args.concat(this.getExprVars(ast.condition));
args = args.concat(this.getStmtsVars(ast.stmts.stmts));
} else if (ast.type === 'for') {
args = args.concat(this.getExprVars(ast.list));
args = args.concat(this.getStmtsVars(ast.stmts.stmts));
} else if (ast.type === 'try') {
args = args.concat(this.getStmtsVars(ast.stmts.stmts));
} else if (ast.type === 'call') {
args = args.concat(this.getExprVars(ast));
} else {
args = args.concat(this.getExprVars(ast));
}
}
return args.filter(arg => {
if(!arg) {
return false;
}
if(declare.includes(arg.name)) {
return false;
}
return true;
});
}
visitAPIBody(ast, level, env) {
assert.equal(ast.type, 'apiBody');
this.emit(`${REQUEST} ${env.runtimeBody ? '' : ':'}= dara.NewRequest()\n`, level);
if (ast.stmts) {
this.visitStmts(ast.stmts, level, env);
}
}
visitRuntimeBefore(ast, level, env) {
assert.equal(ast.type, 'object');
this.emit('_runtime := dara.NewRuntimeObject(', level);
this.visitObject(ast, level, env, 'map[string]interface{}');
this.emit(')\n\n');
this.emit('var retryPolicyContext *dara.RetryPolicyContext\n', level);
this.emit('var request_ *dara.Request\n', level);
this.emit('var response_ *dara.Response\n', level);
this.emit('var _resultErr error\n', level);
this.emit('retriesAttempted := int(0)\n', level);
this.emit('retryPolicyContext = &dara.RetryPolicyContext{\n', level);
this.emit('RetriesAttempted: retriesAttempted,\n', level + 1);
this.emit('}\n\n', level);
if (_name(env.returnType) && _name(env.returnType) !== 'void') {
this.emit(`_result = ${_initValue(_name(env.returnType))}\n`, level);
} else if (env.returnType.path) {
this.emit(`_result = new(`, level);
for (let i = 0; i < env.returnType.path.length; i++) {
const path = env.returnType.path[i];
if (i === 0) {
this.emit(_name(path).toLowerCase());
} else {
this.emit(`.${_name(path)}`);
}
}
this.emit(`)\n`);
} else if (env.returnType.type === 'map') {
this.emit(`_result = make(`, level);
this.visitPointerType(env.returnType, level);
this.emit(`)\n`);
}
this.emit(`for dara.ShouldRetry(_runtime.RetryOptions, retryPolicyContext) {\n`, level);
this.emit(`_resultErr = nil\n`, level + 1);
this.emit(`_backoffDelayTime := dara.GetBackoffDelay(_runtime.RetryOptions, retryPolicyContext)\n`, level + 1);
this.emit(`dara.Sleep(_backoffDelayTime)\n`, level + 1);
this.emit(`\n`);
}
visitStmt(ast, level, env) {
let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]);
this.visitComments(comments, level);
if (ast.type === 'return') {
this.visitReturn(ast, level, env);
} else if (ast.type === 'yield') {
this.visitYield(ast, level, env);
} else if (ast.type === 'if') {
this.visitIf(ast, level, env);
} else if (ast.type === 'throw') {
this.visitThrow(ast, level, env);
} else if (ast.type === 'assign') {
this.visitAssign(ast, level, env);
} else if (ast.type === 'retry') {
this.visitRetry(ast, level);
} else if (ast.type === 'declare') {
this.visitDeclare(ast, level, env);
} else if (ast.type === 'while') {
this.visitWhile(ast, level, env);
} else if (ast.type === 'for') {
this.visitFor(ast, level, env);
} else if (ast.type === 'try') {
this.visitTry(ast, level, env);
} else if (ast.type === 'break') {
this.emit(`break\n`, level);
} else {
if (ast.type === 'call' && ast.hasThrow) {
if (ast.inferred && _name(ast.inferred) !== 'void') {
this.emit(`_, _err ${env.yieldFunc ? ':' : ''}= `, level);
} else {
this.emit(`_err ${env.yieldFunc ? ':' : ''}= `, level);
}
this.visitExpr(ast, level, env, { pointer: false });
this.emit(`\n`);
if(env.runtimeBody){
this.visitAPIErrCatch(level, env);
} else if(env.try) {
const tryStmt = env.try;
env.try = null;
this.visitCatch(tryStmt, level, env);
} else {
this.emit(`if _err != nil {\n`, level);
if (env.returnType && _name(env.returnType) !== 'void') {
this.emit(`return _result, _err\n`, level + 1);
} else if(env.yieldFunc){
this.emit(`_yieldErr <- _err\n`, level + 1);
this.emit(`return\n`, level + 1);
} else {
this.emit(`return _err\n`, level + 1);
}
this.emit(`}\n`, level);
}
} else {
this.emit(``, level);
this.visitExpr(ast, level, env, { pointer: false });
this.emit(`\n`);
}
}
}
getReturnType(stmts, env) {
for (var i = 0; i < stmts.length; i++) {
const ast = stmts[i];
if (ast.type === 'return') {
return env.returnType || { lexeme: 'void' };
}
}
return { lexeme: 'void' };
}
visitCatch(ast, level, env) {
if(ast.finallyBlock) {
env.finallyBlock = true;
this.visitStmts(ast.finallyBlock, level, env);
env.finallyBlock = false;
}
this.emit(`if _err != nil {\n`, level);
if (ast.catchBlocks && ast.catchBlocks.length > 0) {
ast.catchBlocks.forEach(catchBlock => {
if (!catchBlock.id) {
return;
}
if (!catchBlock.id.type) {
this.emit(`if _t, ok := _err.(*dara.SDKError); ok {\n`, level + 1);
} else {
this.emit(`if _t, ok := _err.(`, level + 1);
this.visitType(catchBlock.id.type);
this.emit('); ok {\n');
}
if(catchBlock.catchStmts && catchBlock.catchStmts.stmts.length > 0){
this.emit(`${_name(catchBlock.id)} := _t;\n`, level + 2);
this.visitStmts(catchBlock.catchStmts, level + 2, env);
}
this.emit('}\n', level + 1);
});
} else if (ast.catchBlock && ast.catchBlock.stmts.length > 0) {
this.emit(`if _t, ok := _err.(*dara.SDKError); ok {\n`, level + 1);
this.emit(`${_name(ast.catchId)} := _t\n`, level + 2);
this.emit(`}\n`, level + 1);
this.visitStmts(ast.catchBlock, level + 1, env);
}
this.emit(`}\n`, level);
}
visitTry(ast, level, env) {
assert.equal(ast.type, 'try');
env = env || {
local: new Map(),
};
const tryBlock = ast.tryBlock;
const errCounts = this.stmtsErrCount(tryBlock.stmts);
if(errCounts > 1) {
const funcName = `${_lowerFirst(env.funcName)}_opTryFunc`;
const args = this.getStmtsVars(tryBlock.stmts);
const returnType = this.getReturnType(tryBlock.stmts, env);
this.tryFunc.push({
args,
functionBody: tryBlock,
returnType,
name: funcName,
pointerParams: env.pointerParams,
});
if (returnType !== 'void') {
this.emit(`_result, _err = ${funcName}`, level);
} else {
this.emit(`_err = ${funcName}`, level);
}
this.visitTryArgs(args, level, env);
this.emit('\n');
this.visitCatch(ast, level, env);
env.hasReturn = true;
return;
}
env.try = ast;
this.visitStmts(tryBlock, level, env);
}
visitWhile(ast, level, env) {
assert.equal(ast.type, 'while');
let argHasThrowFunc;
if (ast.condition.type === 'not' && ast.condition.expr && ast.condition.expr.type === 'call') {
argHasThrowFunc = this.visitFunctionNested(ast.condition.expr, level, env);
} else if (ast.condition.type === 'call') {
argHasThrowFunc = this.visitFunctionNested(ast.condition, level, env);
}
this.emit('for ', level);
let setFunc;
if(ast.condition && ast.condition.type === 'call') {
let dealFunc = this.getVarDealFunc(ast.condition, true);
setFunc = dealFunc && dealFunc(_name(ast.condition.inferred));
}
if(setFunc) {
this.emit(`${setFunc}`);
}
this.visitExpr(ast.condition, level + 1, env, false, argHasThrowFunc);
if(setFunc) {
this.emit(')');
}
this.emit(' {\n');
this.visitStmts(ast.stmts, level + 1, env);
this.emit('}\n', level);
}
visitFor(ast, level, env) {
assert.equal(ast.type, 'for');
if(ast.list.inferred && _isIterator(ast.list.inferred)) {
this.emit(`for ${_name(ast.id)} := range `, level);
} else {
this.emit(`for _, ${_name(ast.id)} := range `, level);
}
this.visitExpr(ast.list, level + 1, env, { pointer: true });
this.emit(' {\n');
this.visitStmts(ast.stmts, level + 1, env);
this.emit('}\n', level);
}
visitFieldValue(ast, structName, level) {
if (ast.type === 'fieldType') {
if (ast.fieldType === 'array') {
if (ast.fieldItemType.type === 'modelBody') {
this.emit('{\n');
this.visitModelBody(ast.fieldItemType, ast.fieldItemType.nodes, structName, level);
}
return;
}
}
if (ast.type === 'modelBody') {
this.emit('{\n');
this.visitModelBody(ast, ast.nodes, structName, level);
return;
}
throw new Error('unimpelemented');
}
visitType(ast, level) {
if (ast.type === 'moduleModel') {
const [ mainId, ...rest ] = ast.path;
let moduleName = _importFilter(_name(ast.path[0]).toLowerCase());
let modelName = rest.map(node => {
return _upperFirst(_name(node));
}).join('');
const externEx = this.usedExternException.get(_name(mainId));
if (externEx && externEx.has(modelName)) {
modelName += 'Error';
}
this.emit(`*${moduleName}.${_format(modelName)}`);
} else if (ast.type === 'moduleTypedef') {
this.emit(`*`);
for (let i = 1; i < ast.path.length; i++) {
this.emit(`${this.typeRelover(ast.path[i], ast.path[0])}`);
}
} else if (ast.type === 'subModel') {
this.emit(`*${_format(_name(ast.path[0]))}`);
for (let i = 1; i < ast.path.length; i++) {
this.emit(`${_format(_name(ast.path[i]))}`);
}
} else if ((ast.type === 'map' || ast.fieldType === 'map') && _name(ast.keyType)) {
this.emit(`map[${this.getType(_name(ast.keyType), false)}]`);
this.visitPointerType(ast.valueType, level);
} else if (ast.fieldType === 'array' || ast.type === 'array') {
this.emit(`[]`);
this.visitPointerType(ast.subType || ast.itemType, level);
} else if (ast.idType === 'module' || this.clientName[_name(ast)]) {
this.emit(`${this.clientName[_name(ast)]}`);
} else if (this.typeRelover(ast)) {
this.emit(this.getType(this.typeRelover(ast), false));
} else if (ast.fieldType && DSL.util.isBasicType(ast.fieldType)) {
this.emit(this.getType(ast.fieldType, false));
} else if (ast.fieldType && this.typeRelover(ast.fieldType)) {
this.emit(this.getType(this.typeRelover(ast.fieldType), false));
} else {
this.emit(this.getType(ast, false));
}
}
visitModuleName(aliasId) {
const moduleName = _importFilter(_format(_name(aliasId)).toLowerCase());
this.emit(`${moduleName}`);
const { pkgName } = this.module.importPackages[_name(aliasId)];
this.imports.push({
aliasId: _name(aliasId),
pkgName,
});
}
visitPointerType(ast, level) {
if (ast.type === 'moduleModel') {
this.emit('*');
this.visitModuleName(ast.path[0]);
for (let i = 1; i < ast.path.length; i++) {
if (i === 1) {
this.emit(`.`);
}
this.emit(`${_format(_name(ast.path[i]))}`);
}
} else if (ast.type === 'moduleTypedef') {
this.emit(`*`);
for (let i = 1; i < ast.path.length; i++) {
this.emit(`${this.typeRelover(ast.path[i], ast.path[0])}`);
}
} else if (ast.type === 'subModel') {
this.emit(`*${_format(_name(ast.path[0]))}`);
for (let i = 1; i < ast.path.length; i++) {
this.emit(`${_format(_name(ast.path[i]))}`);
}
} else if ((ast.type === 'map' || ast.fieldType === 'map') && _name(ast.keyType)) {
this.emit(`map[${this.getType(_name(ast.keyType), false)}]`);
this.visitPointerType(ast.valueType, level);
} else if (ast.type === 'asyncIterator' || ast.type === 'iterator') {
this.visitPointerType(ast.valueType, level);
}else if (ast.fieldType === 'array' || ast.type === 'array') {
this.emit(`[]`);
this.visitPointerType(ast.subType || ast.itemType, level);
} else if (ast.idType === 'module' || this.clientName[_name(ast)]) {
this.emit(`${this.clientName[_name(ast)]}`);
} else if (ast.idType === 'builtin_model') {
this.emit(`${this.getType(_name(ast), false)}`);
} else if (ast.type === 'model') {
this.emit(`*`);
if (ast.moduleName) {
this.emit(`${ast.moduleName.replace(/-/g, '_').toLowerCase()}.`);
}
let strs = _format(_name(ast)).split('.');
strs.forEach(str => {
this.emit(`${_modelName(_format(str))}`);
});
} else if (_name(ast)) {
this.emit(this.getType(this.typeRelover(ast)));
} else if (ast.fieldType && DSL.util.isBasicType(ast.fieldType)) {
this.emit(this.getType(ast.fieldType));
} else if (ast.fieldType && this.typeRelover(ast.fieldTyp)) {
this.emit(this.getType(this.typeRelover(ast.fieldType)));
} else {
this.emit(this.getType(ast));
}
}
visitModelField(ast, structName, level) {
//assert.equal(ast.fieldValue.type, 'fieldType');
this.emit(`type ${structName} struct `);
this.visitFieldValue(ast, structName, level);
}
visitFieldType(node, structName, fields, structMap, level) {
let type = '', omitemptyEnable = true;
if (node.fieldValue.fieldType === 'array') {
type = `type:"Repeated"`;
if (this.config.go && this.config.go.mapAndSliceWithoutOmitempty === true) {
omitemptyEnable = false;
}
if (_name(node.fieldValue.fieldItemType)) {
this.emit(`[]${this.getType(_name(node.fieldValue.fieldItemType))} `);
} else if (node.fieldValue.fieldItemType.type === 'map') {
this.emit(`[]`);
this.visitType(node.fieldValue.fieldItemType);
this.emit(` `);
} else if (node.fieldValue.fieldItemType.type === 'modelBody') {
structMap.push(structName);
this.emit(`[]*${structName} `);
fields.push(node.fieldValue);
} else if (node.fieldValue.fieldItemType.fieldType === 'array') {
this.emit(`[][]`);
this.emitModelArray(node.fieldValue.fieldItemType, structMap, fields, structName);
}
} else if (node.fieldValue.type === 'modelBody') {
this.emit(`*${structName} `);
structMap.push(structName);
fields.push(node.fieldValue);
type = `type:"Struct"`;
} else {
const fieldType = node.fieldValue.fieldType;
if (!_name(fieldType) && (fieldType === 'map' || fieldType === 'object')) {
if (this.config.go && this.config.go.mapAndSliceWithoutOmitempty === true) {
omitemptyEnable = false;
}
this.visitPointerType(node.fieldValue, level);
this.emit(` `);
} else {
this.visitPointerType(fieldType, level);
this.emit(` `);
}
}
return { type, omitemptyEnable };
}
visitModelBody(ast, nodes, lastName, level) {
assert.equal(ast.type, 'modelBody');
var fields = [];
const structMap = [];
let node;
for (let i = 0; i < nodes.length; i++) {
node = nodes[i];
let comments = DSL.comment.getFrontComments(this.comments, node.tokenRange[0]);
this.visitComments(comments, level);
var fieldName = _name(node.fieldName);
const structName = lastName + _format(fieldName);
const description = getAttr(node, 'description');
const example = getAttr(node, 'example');
const checkBlank = getAttr(node, 'checkBlank');
const nullable = getAttr(node, 'nullable');
const sensitive = getAttr(node, 'sensitive');
const deprecated = getAttr(node, 'deprecated');
let hasNextSection = false;
if (deprecated === 'true') {
this.emit(`// Deprecated\n`, level);
hasNextSection = true;
}
if (description || example || typeof checkBlank !== 'undefined' || typeof nullable !== 'undefined' || typeof sensitive !== 'undefined') {
if (description) {
if (hasNextSection) {
this.emit(`// \n`, level);
}
const descriptions = _escape(description).split('\n');
for (let j = 0; j < descriptions.length; j++) {
if (descriptions[j] === '') {
this.emit(`// \n`, level);
}
else {
this.emit(`// ${descriptions[j]}\n`, level);
if (j < descriptions.length - 1 && descriptions[j + 1] !== '') {
this.emit(`// \n`, level);
}
}
}
hasNextSection = true;
}
if (typeof checkBlank !== 'undefined') {
if (hasNextSection) {
this.emit(`// \n`, level);
}
this.emit('// check if is blank:\n', level);
this.emit(`// ${checkBlank}\n`, level);
hasNextSection = true;
}
if (typeof nullable !== 'undefined') {
if (hasNextSection) {
this.emit(`// \n`, level);
}
this.emit('// if can be null:\n', level);
this.emit(`// ${nullable}\n`, level);
hasNextSection = true;
}
if (typeof sensitive !== 'undefined') {
if (hasNextSection) {
this.emit(`// \n`, level);
}
this.emit('// if sensitive:\n', level);
this.emit(`// ${sensitive}\n`, level);
hasNextSection = true;
}
if (example) {
if (hasNextSection) {
this.emit(`// \n`, level);
}
const examples = _escape(example).split('\n');
this.emit('// example:\n', level);
this.emit(`// \n`, level);
for (let j = 0; j < examples.length; j++) {
if (examples[j] === '') {
this.emit(`// \n`, level);
} else {
this.emit(`// ${examples[j]}\n`, level);
if (j < examples.length - 1 && examples[j + 1] !== '') {
this.emit(`// \n`, level);
}
}
}
}
}
this.emit(`${_format(fieldName)} `, level);
let { type, omitemptyEnable } = this.visitFieldType(node, structName, fields, structMap, level);
var realName = _getAttr(node, 'name');
if (!realName) {
realName = fieldName;
}
var tag = `json:"${realName}${omitemptyEnable ? ',omitempty' : ''}" xml:"${realName}${omitemptyEnable ? ',omitempty' : ''}"`;
const anno = this.parseAnnotation(node, {
'signed': 'string', 'encode': 'string'
, 'pattern': 'string', 'maxLength': 'value', 'minLength': 'value',
'maximum': 'value', 'minimum': 'value'
});
if (node.required) {
tag = tag + ` require:"true"`;
}
if (anno !== '') {
tag = tag + anno;
}
if (type !== '') {
tag = tag + ` ${type}`;
}
this.emit(`\`${tag}\``);
this.emit(`\n`);
}
if (node) {
//find the last node's back comment
let comments = DSL.comment.getBetweenComments(this.comments, node.tokenRange[0], ast.tokenRange[1]);
this.visitComments(comments, level);
}
if (ast.nodes.length === 0) {
//empty block's comment
let comments = DSL.comment.getBetweenComments(this.comments, ast.tokenRange[0], ast.tokenRange[1]);
this.visitComments(comments, level);
}
this.emit(`}\n`);
this.emit(`\n`);
this.eachGetFunc(nodes, lastName, 'model');
this.eachSetFunc(nodes, lastName);
this.visitValidate(nodes, lastName);
for (let i = 0; i < fields.length; i++) {
this.visitModelField(fields[i], structMap[i], level);
}
}
emitModelArray(node, structMap, fields, structName) {
if (node.fieldItemType.fieldType === 'array') {
this.emit(`[]`);
this.emitModelArray(node, structMap, fields, structName);
} else if (node.fieldItemType.type === 'modelBody') {
structMap.push(structName);
this.emit(`*${structName} `);
fields.push(node);
} else {
this.visitPointerType(node.fieldItemType);
this.emit(` `);
}
}
checkAnnotation(node, attrName) {
for (let i = 0; i < node.attrs.length; i++) {
if (attrName === _name(node.attrs[i].attrName)) {
return true;
}
}
return false;
}
parseAnnotation(node, annos) {
var tag = '';
for (let i = 0; i < node.attrs.length; i++) {
const attrValueType = annos[_name(node.attrs[i].attrName)];
if (attrValueType) {
var attrName = _name(node.attrs[i].attrName);
attrName = attrName.split('-').join('');
tag = tag + ` ${attrName}:"${node.attrs[i].attrValue[attrValueType]}"`;
}
}
return tag;
}
emitFuncArray(node, structName) {
if (node.fieldItemType.fieldType === 'array') {
this.emit(`[]`);
this.emitFuncArray(node, structName);
} else if (node.fieldItemType.type === 'modelBody') {
this.emit(`*${structName}`);
} else {
this.visitPointerType(node.fieldItemType);
}
}
eachGetFunc(nodes, structName, type = 'model', level = 0) {
if(type === 'model') {
this.emit(`func (s ${structName}) String() string {\n`, level);
this.emit(`return dara.Prettify(s)\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
this.emit(`func (s ${structName}) GoString() string {\n`, level);
this.emit(`return s.String()\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
} else {
this.builtinModule.push({
path: 'fmt'
});
this.emit(`func (err ${structName}Error) Error() string {\n`, level);
this.emit('if err.Message == nil {\n', level + 1);
this.emit(`str := fmt.Sprintf("${structName}Error:\\n Name: %s\\n Code: %s\\n",\n`, level + 2);
this.emit('dara.StringValue(err.Name), dara.StringValue(err.Code))\n', level + 3);
this.emit('err.Message = dara.String(str)\n', level + 2);
this.emit(`}\n`, level + 1);
this.emit(`return dara.StringValue(err.Message)\n`, level + 1);
this.emit(`}\n\n`, level);
}
// for (let i = 0; i < ast.extendFileds.length; i++) {
// const node = ast.extendFileds[i];
// this.visitGetFunc(node, structName, level);
// }
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
this.visitGetFunc(node, structName, type, level);
}
}
visitGetFunc(ast, structName, type = 'model',level = 0){
const fieldName = _format(_name(ast.fieldName));
this.emit(`func (s *${structName}${type === 'model' ? '' : 'Error'}) Get${fieldName}() `, level);
structName = structName + _format(fieldName);
this.visitFieldType(ast, structName, [], [], level);
this.emit(' {\n');
this.emit(`return s.${fieldName}\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
}
eachSetFunc(nodes, structName, level = 0) {
// for (let i = 0; i < ast.extendFileds.length; i++) {
// const node = ast.extendFileds[i];
// this.visitSetFunc(node, structName, level);
// }
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
this.visitSetFunc(node, structName, level);
}
}
visitValidate(nodes, structName, level = 0) {
this.emit(`func (s *${structName}) Validate() error {\n`, level);
this.emit(`return dara.Validate(s)\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
}
visitSetFunc(ast, structName, level = 0){
const fieldName = _format(_name(ast.fieldName));
const fileldtype = structName + _format(fieldName);
const itemName = structName + _format(fieldName);
if (ast.fieldValue.fieldType === 'array') {
if (_name(ast.fieldValue.fieldItemType)) {
this.emit(`func (s *${structName}) Set${fieldName}(v []${this.getType(_name(ast.fieldValue.fieldItemType))}) *${structName} {\n`, level);
this.emit(`s.${fieldName} = v\n`, level + 1);
this.emit(`return s\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
} else if (ast.fieldValue.fieldItemType.type === 'map') {
this.emit(`func (s *${structName}) Set${fieldName}(v []`, level);
this.visitType(ast.fieldValue.fieldItemType);
this.emit(`) *${structName} {\n`);
this.emit(`s.${fieldName} = v\n`, level + 1);
this.emit(`return s\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
} else if (ast.fieldValue.fieldItemType.type === 'modelBody') {
this.emit(`func (s *${structName}) Set${fieldName}(v []${this.getType(fileldtype)}) *${structName} {\n`, level);
this.emit(`s.${fieldName} = v\n`, level + 1);
this.emit(`return s\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
} else if (ast.fieldValue.fieldItemType.fieldType === 'array') {
this.emit(`func (s *${structName}) Set${fieldName}(v [][]`, level);
this.emitFuncArray(ast.fieldValue.fieldItemType, itemName);
this.emit(`) *${structName} {\n`);
this.emit(`s.${fieldName} = v\n`, level + 1);
this.emit(`return s\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
}
} else if (ast.fieldValue.type === 'modelBody') {
this.emit(`func (s *${structName}) Set${fieldName}(v *${fileldtype}) *${structName} {\n`, level);
this.emit(`s.${fieldName} = v\n`, level + 1);
this.emit(`return s\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
} else if (_name(ast.fieldValue.fieldType) && ast.fieldValue.fieldType.idType === 'module') {
const fieldType = ast.fieldValue.fieldType;
this.emit(`func (s *${structName}) Set${fieldName}(v ${this.clientName[_name(fieldType)]}) *${structName} {\n`, level);
this.emit(`s.${fieldName} = v\n`, level + 1);
this.emit(`return s\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
} else if (ast.fieldValue.fieldType.type === 'moduleModel' || ast.fieldValue.fieldType.type === 'moduleTypedef' || ast.fieldValue.fieldType.type === 'subModel') {
this.emit(`func (s *${structName}) Set${fieldName}(v `, level);
this.visitType(ast.fieldValue.fieldType, level);
this.emit(`) *${structName} {\n`, level);
this.emit(`s.${fieldName} = v\n`, level + 1);
this.emit(`return s\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
} else {
var fieldType = '';
if (!_name(ast.fieldValue.fieldType)) {
fieldType = ast.fieldValue.fieldType;
} else {
fieldType = _name(ast.fieldValue.fieldType);
}
this.emit(`func (s *${structName}) Set${fieldName}(v `, level);
this.visitType(ast.fieldValue, level, {});
this.emit(`) *${structName} {\n`, level);
if (!DSL.util.isBasicType(fieldType) || _isFilterType(fieldType)) {
this.emit(`s.${fieldName} = v\n`, level + 1);
} else {
this.emit(`s.${fieldName} = &v\n`, level + 1);
}
this.emit(`return s\n`, level + 1);
this.emit(`}\n`, level);
this.emit(`\n`, level);
}
}
visitExtendOn(extendOn, level, type = 'model') {
if(!extendOn) {
type === 'exception' ? this.emit(`dara.${CORE}Error\n`, level) : this.emit(`dara.Model\n`, level);
return;
}
switch(_name(extendOn)) {
case '$Error':
this.emit(`dara.${CORE}Error\n`, level);
return;
case '$ResponseError':
this.emit(`dara.ResponseError\n`, level);
return;
case '$Model':
this.emit(`dara.Model\n`, level);
return;
}
if (extendOn.type === 'moduleModel') {
const [moduleId, ...rest] = extendOn.path;
this.emit(`${_importFilter(_name(moduleId).toLowerCase())}.`, level);
this.emit('i');
this.emit(rest.map((item) => {
return _format(_name(item));
}).join(''));
} else if (extendOn.type === 'subModel') {
this.emit(extendOn.path.map((item) => {
return _format(_name(item));
}).join(''), level);
const [moduleId, ...rest] = extendOn.path;
this.emit(`i${_format(_name(moduleId))}`);
this.emit(rest.map((item) => {
return _format(_name(item));
}).join(''));
} else {
this.emit('i', level);
if (extendOn.moduleName) {
this.emit(`${_importFilter(_name(extendOn.moduleName).toLowerCase())}.`);
}
this.emit(`${_modelName(_format(_name(extendOn)))}`);
}
if(type === 'exception') {
this.emit('Error');
}
this.emit('\n');
}
dealExtendFileds(ast) {
const fileds = [];
for (let i = 0; i < ast.nodes.length; i++) {
const node = ast.nodes[i];
const fieldName = _name(node.fieldName);
fileds.push(fieldName);
}
const extendFileds = [];
for (let i = 0; i < ast.extendFileds.length; i++) {
const node = ast.extendFileds[i];
node.extend = true;
const fieldName = _name(node.fieldName);
if(fileds.includes(fieldName)) {
continue;
}
extendFileds.push(node);
}
return extendFileds.concat(ast.nodes);
}
visitExceptionBody(ast, nodes, lastName, level) {
assert.equal(ast.type, 'exceptionBody');
const fields = [];
const structMap = [];
let node;
for (let i = 0; i < nodes.length; i++) {
node = nodes[i];
if(!node.extend) {
let comments = DSL.comment.getFrontComments(this.comments, node.tokenRange[0]);
this.visitComments(comments, level);
}
if(i !== nodes.length - 1) {
node.extend = false;
}
var fieldName = _name(node.fieldName);
let tag = '';
const structName = lastName + _format(fieldName);
this.emit(`${_format(fieldName)} `, level);
const { type } = this.visitFieldType(node, structName, fields, structMap, level);
var realName = _getAttr(node, 'name');
if (!realName) {
realName = fieldName;
}
if (node.required) {
tag = tag + ` require:"true"`;
}
if (type !== '') {
tag = tag + ` ${type}`;
}
this.emit(`\`${tag}\``);
this.emit(`\n`);
}
if (node && !node.extend) {
//find the last node's back comment
let comments = DSL.comment.getBetweenComments(this.comments, node.tokenRange[0], ast.tokenRange[1]);
this.visitComments(comments, level);
}
if (ast.nodes.length === 0) {
//empty block's comment
let comments = DSL.comment.getBetweenComments(this.comments, ast.tokenRange[0], ast.tokenRange[1]);
this.visitComments(comments, level);
}
this.emit(`}\n`);
this.emit(`\n`);
this.eachGetFunc(nodes, lastName, 'exception');
for (let i = 0; i < fields.length; i++) {
this.visitModelField(fields[i], structMap[i], level);
}
}
visitException(ast, level) {
assert.equal(ast.type, 'exception');
const exceptionName = _upperFirst(_name(ast.exceptionName));
this.visitAnnotation(ast.annotation, level);
let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]);
this.visitComments(comments, level);
this.emit(`type ${exceptionName}Error struct {\n`, level);
// this.visitExtendOn(ast.extendOn, level + 1, 'exception');
const nodes = this.dealExtendFileds(ast.exceptionBody);
this.visitExceptionBody(ast.exceptionBody, nodes, exceptionName, level + 1);
}
visitExceptionInterface(ast, level) {
assert.equal(ast.type, 'exception');
const exceptionName = _upperFirst(_name(ast.exceptionName));
this.emit(`type i${exceptionName}Error interface {\n`, level);
// this.visitExtendOn(ast.extendOn, level + 1, 'exception');
const nodes = this.dealExtendFileds(ast.exceptionBody);
this.visitExceptionInterfceBody(ast.exceptionBody, nodes, exceptionName, level + 1);
this.emit('}\n\n', level);
}
visitExceptionInterfceBody(ast, nodes, structName, level) {
assert.equal(ast.type, 'exceptionBody');
this.emit(`Error() string\n`, level);
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
const fieldName = _format(_name(node.fieldName));
this.emit(`Get${fieldName}() `, level);
this.visitFieldType(node, structName + fieldName, [], [], level);
this.emit('\n');
}
}
visitModelInterface(ast, level) {
assert.equal(ast.type, 'model');
const modelName = _format(_name(ast.modelName));
this.emit(`type i${modelName} interface {\n`, level);
this.visitExtendOn(ast.extendOn, level + 1, 'model');
this.visitModelInterfaceBody(ast.modelBody, modelName, level + 1);
this.emit('}\n\n', level);
}
visitModelInterfaceBody(ast, structName, level) {
assert.equal(ast.type, 'modelBody');
this.emit(`String() string\n`, level);
this.emit(`GoString() string\n`, level);
for (let i = 0; i < ast.nodes.length; i++) {
const node = ast.nodes[i];
const fieldName = _format(_name(node.fieldName));
const fileldtype = structName + _format(fieldName);
const itemName = structName + _format(fieldName);
if (node.fieldValue.fieldType === 'array') {
if (_name(node.fieldValue.fieldItemType)) {
this.emit(`Set${fieldName}(v []${this.getType(_name(node.fieldValue.fieldItemType))}) *${structName}\n`, level);
} else if (node.fieldValue.fieldItemType.type === 'map') {
this.emit(`Set${fieldName}(v []`, level);
this.visitType(node.fieldValue.fieldItemType);
this.emit(`) *${structName}\n`);
} else if (node.fieldValue.fieldItemType.type === 'modelBody') {
this.emit(`Set${fieldName}(v []${this.getType(fileldtype)}) *${structName}\n`, level);
} else if (node.fieldValue.fieldItemType.fieldType === 'array') {
this.emit(`Set${fieldName}(v [][]`, level);
this.emitFuncArray(node.fieldValue.fieldItemType, itemName);
this.emit(`) *${structName}\n`);
}
} else if (node.fieldValue.type === 'modelBody') {
this.emit(`Set${fieldName}(v *${fileldtype}) *${structName}\n`, level);
} else if (_name(node.fieldValue.fieldType) && node.fieldValue.fieldType.idType === 'module') {
const fieldType = node.fieldValue.fieldType;
this.emit(`Set${fieldName}(v ${this.clientName[_name(fieldType)]}) *${structName}\n`, level);
} else if (node.fieldValue.fieldType.type === 'moduleModel' || node.fieldValue.fieldType.type === 'moduleTypedef' || node.fieldValue.fieldType.type === 'subModel') {
this.emit(`Set${fieldName}(v `, level);
this.visitType(node.fieldValue.fieldType, level);
this.emit(`) *${structName}\n`);
} else {
this.emit(`Set${fieldName}(v `, level);
this.visitType(node.fieldValue, level, {});
this.emit(`) *${structName}\n`);
}
this.emit(`Get${fieldName}() `, level);
this.visitFieldType(node, itemName, [], [], level);
this.emit('\n');
}
}
visitModel(ast, level) {
assert.equal(ast.type, 'model');
const modelName = _format(_name(ast.modelName));
this.visitAnnotation(ast.annotation, level);
let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]);
this.visitComments(comments, level);
this.emit(`type ${modelName} struct {\n`, level);
// this.visitExtendOn(ast.extendOn, level + 1, 'model');
const nodes = this.dealExtendFileds(ast.modelBody);
this.visitModelBody(ast.modelBody, nodes, modelName, level + 1);
}
getModelFileds(ast) {
const fileds = {};
for (let i = 0; i < ast.modelBody.nodes.length; i++) {
const node =ast.modelBody.nodes[i];
const fieldName = _name(node.fieldName);
fileds[fieldName] = node;
}
if(_name(ast.extendOn) === '$Error') {
this.emit(`${_modelName(_format(_name(ast.extendOn)))}\n`);
return;
}
}
visitObjectFieldValue(ast, level, env, expected) {
let setFunc;
if(ast && ast.type === 'call') {
let dealFunc = this.getVarDealFunc(ast, expected);
setFunc = dealFunc && dealFunc(_name(ast.inferred));
}
if(setFunc) {
this.emit(`${setFunc}`);
}
this.visitExpr(ast, level, env, expected);
if(setFunc) {
this.emit(')');
}
}
visitObjectField(ast, level, env, expected) {
assert.equal(ast.type, 'objectField');
let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]);
this.visitComments(comments, level);
var key = _name(ast.fieldName) || _string(ast.fieldName);
this.emit(`"${key}": `, level);
this.visitObjectFieldValue(ast.expr, level, env, expected);
this.emit(`,\n`);
}
visitObject(ast, level, env, expected) {
assert.equal(ast.type, 'object');
if (ast.fields.length === 0) {
if (ast.inferred && ast.inferred.type === 'map' &&
!(_name(ast.inferred.keyType) === 'string' && _name(ast.inferred.valueType) === 'any')) {
this.emit(`make(`);
this.visitType(ast.inferred, level, env);
this.emit(`)`);
} else if (ast.inferred && ast.inferred.type === 'model') {
this.emit('{}');
} else if (ast.inferred && ast.inferred.type === 'array') {
this.emit(`make(`);
this.visitPointerType(expected.type ? expected : ast.inferred, level, env);
this.emit(`, 0)`);
} else {
this.emit('map[string]interface{}{');
let comments = DSL.comment.getBetweenComments(this.comments, ast.tokenRange[0], ast.tokenRange[1]);
if (comments.length > 0) {
this.emit('\n');
this.visitComments(comments, level + 1);
this.emit('', level);
}
this.emit(`}`);
}
} else {
var hasExpandField = false;
for (var i = 0; i < ast.fields.length; i++) {
const field = ast.fields[i];
if (!field) {
continue;
}
if (field.type === 'expandField') {
hasExpandField = true;
break;
}
}
var fieldType = false;
if (!hasExpandField) {
if (expected === 'map[string]interface{}') {
this.emit('map[string]interface{}{\n');
} else if (ast.inferred && ast.inferred.type === 'map') {
expected = ast.inferred.valueType;
if (!_isFilterType(_name(expected))) {
fieldType = true;
}
this.visitType(ast.inferred, level, env);
this.emit(`{\n`);
} else if (ast.inferred && ast.inferred.type === 'model') {
this.emit('{\n');
} else if (ast.inferred && ast.inferred.type === 'array') {
if (ast.inferred.itemType !== 'any') {
fieldType = true;
}
this.visitPointerType(expected.type ? expected : ast.inferred, level, env);
this.emit(`{\n`);
} else {
this.emit('map[string]interface{}{\n');
}
for (i = 0; i < ast.fields.length; i++) {
this.visitObjectField(ast.fields[i], level + 1, env, expected.type ? { ...expected, pointer: fieldType } : { pointer: fieldType });
}
let comments = DSL.comment.getBetweenComments(this.comments, ast.fields[i - 1].tokenRange[0], ast.tokenRange[1]);
this.visitComments(comments, level + 1);
this.emit('}', level);
return;
}
const fields = ast.fields.filter((field) => {
return field.type === 'objectField';
});
const expandFields = ast.fields.filter((field) => {
return field.type === 'expandField';
});
var isMerge = false;
if (expandFields.length > 0) {
isMerge = true;
if (ast.inferred && ast.inferred.type === 'map' &&
ast.inferred.valueType.name === 'string') {
this.emit('dara.Merge(');
} else {
this.emit('dara.ToMap(');
}
}
if (fields.length > 0) {
var isFieldPointer = false;
if (!_isFilterType(_name(ast.inferred.valueType))) {
isFieldPointer = true;
}
this.visitType(ast.inferred, level, env);
this.emit(`{\n`);
for (i = 0; i < fields.length; i++) {
this.visitObjectField(fields[i], level + 1, env, { pointer: isFieldPointer });
}
if (isMerge) {
//find the last item's back comment
let comments = DSL.comment.getBetweenComments(this.comments, ast.fields[i - 1].tokenRange[0], ast.tokenRange[1]);
this.visitComments(comments, level + 1);
this.emit('}, ', level);
} else {
//find the last item's back comment
let comments = DSL.comment.getBetweenComments(this.comments, ast.fields[i - 1].tokenRange[0], ast.tokenRange[1]);
this.visitComments(comments, level + 1);
this.emit('}', level);
}
}
for (let i = 0; i < expandFields.length; i++) {
this.visitExpr(expandFields[i].expr, level + 1, env);
if (expandFields.length > 1 && i < expandFields.length - 1) {
this.emit(',\n');
this.emit(``, level + 1);
}
}
if (isMerge) {
this.emit(')');
}
}
}
visitArgs(args, level, env, expected, argHasThrowFunc) {
this.emit('(');
for (let i = 0; i < args.length; i++) {
const expr = args[i];
if(expr.yieldArg) {
this.visitYieldArgs(expr.ast, level, env);
} else if (expr.needCast) {
this.emit('dara.ToMap(');
if (argHasThrowFunc && argHasThrowFunc.get(i)) {
this.emit(argHasThrowFunc.get(i));
} else {
this.visitExpr(expr, level, env);
}
this.emit(')');
} else {
if ((expr.expectedType.name === 'number' || expr.expectedType.name === 'integer') && expr.inferred.name === 'int32') {
this.emit(`dara.ToInt(`);
if (argHasThrowFunc && argHasThrowFunc.get(i)) {
this.emit(argHasThrowFunc.get(i));
} else {
this.visitExpr(expr, level, env, expected);
}
this.emit(`)`);
} else if (expr.type !== 'number' && (expr.inferred.name === 'number' || expr.inferred.name === 'integer') && expr.expectedType.name === 'int32') {
this.emit(`dara.ToInt32(`);
if (argHasThrowFunc && argHasThrowFunc.get(i)) {
this.emit(argHasThrowFunc.get(i));
} else {
this.visitExpr(expr, level, env, expected);
}
this.emit(`)`);
} else {
let setFunc;
if(expr && expr.type === 'call') {
let dealFunc = this.getVarDealFunc(expr, expected);
setFunc = dealFunc && dealFunc(_name(expr.inferred));
}
if(setFunc) {
this.emit(`${setFunc}`);
}
if (argHasThrowFunc && argHasThrowFunc.get(i)) {
this.emit(argHasThrowFunc.get(i));
} else {
this.visitExpr(expr, level, env, expected);
}
if(setFunc) {
this.emit(')');
}
}
}
if (i !== args.length - 1) {
this.emit(', ');
}
}
this.emit(')');
}
visitMethodCall(ast, level, env, argHasThrowFunc) {
assert.equal(ast.left.type, 'method_call');
const name = _format(_name(ast.left.id));
if (name.startsWith('$') && this.builtin[name]) {
const method = name.replace('$', '');
this.builtin[name][method](ast.args, level, env, argHasThrowFunc);
return;
}
if (!ast.isStatic) {
this.emit(`client.`);
}
this.emit(name);
this.visitArgs(ast.args, level, env, { pointer: true }, argHasThrowFunc);
}
visitInstanceCall(ast, level, env, argHasThrowFunc) {
assert.equal(ast.left.type, 'instance_call');
const method = _name(ast.left.propertyPath[0]);
const builtinInstance = ast.builtinModule && this.builtin[ast.builtinModule];
if(builtinInstance && this.builtin[ast.builtinModule][method]) {
this.builtin[ast.builtinModule][method](ast, level, {}, argHasThrowFunc, { pointer: false });
return;
}
if (_name(ast.left.id).indexOf('@') === 0) {
this.emit(`client.${_format(_name(ast.left.id).substring(1))}.${_format(method)}`);
} else {
this.emit(`${_name(ast.left.id)}.${_format(method)}`);
}
this.visitArgs(ast.args, level, env, { pointer: builtinInstance ? false : true }, argHasThrowFunc);
}
visitCall(ast, level, env, argHasThrowFunc) {
assert.equal(ast.type, 'call');
if (ast.left.type === 'method_call') {
this.visitMethodCall(ast, level, env, argHasThrowFunc);
} else if (ast.left.type === 'instance_call') {
this.visitInstanceCall(ast, level, env, argHasThrowFunc);
} else if (ast.left.type === 'static_call') {
this.visitStaticCall(ast, level, env, argHasThrowFunc);
} else {
throw new Error('un-implemented');
}
}
visitBuiltinStaticCall(ast, level, env, argHasThrowFunc) {
const moduleName = _name(ast.left.id);
const builtiner = this.builtin[moduleName];
if(!builtiner) {
throw new Error('un-implemented');
}
const func = _name(ast.left.propertyPath[0]);
builtiner[func](ast.args, level, env, argHasThrowFunc);
}
visitStaticCall(ast, level, env, argHasThrowFunc) {
assert.equal(ast.left.type, 'static_call');
if(ast.left.id.type === 'builtin_module') {
this.visitBuiltinStaticCall(ast, level, env, argHasThrowFunc);
return;
}
this.visitModuleName(ast.left.id);
this.emit(`.${_format(_name(ast.left.propertyPath[0]))}(`);
for (let i = 0; i < ast.args.length; i++) {
const expr = ast.args[i];
if(expr.yieldArg) {
this.visitYieldArgs(expr.ast, level, env);
} else if (expr.needCast) {
this.emit('dara.ToMap(');
if (argHasThrowFunc && argHasThrowFunc.get(i)) {
this.emit(argHasThrowFunc.get(i));
} else {
this.visitExpr(expr, level, env);
}
this.emit(')');
} else {
if ((expr.expectedType.name === 'number' || expr.expectedType.name === 'integer') && expr.inferred.name === 'int32') {
this.emit(`dara.ToInt(`);
if (argHasThrowFunc && argHasThrowFunc.get(i)) {
this.emit(argHasThrowFunc.get(i));
} else {
this.visitExpr(expr, level, env, { pointer: true });
}
this.emit(`)`);
} else if (expr.type !== 'number' && (expr.inferred.name === 'number' || expr.inferred.name === 'integer') && expr.expectedType.name === 'int32') {
this.emit(`dara.ToInt32(`);
if (argHasThrowFunc && argHasThrowFunc.get(i)) {
this.emit(argHasThrowFunc.get(i));
} else {
this.visitExpr(expr, level, env, { pointer: true });
}
this.emit(`)`);
} else {
let setFunc;
if(expr && expr.type === 'call') {
let dealFunc = this.getVarDealFunc(expr, { pointer: true });
setFunc = dealFunc && dealFunc(_name(expr.inferred));
}
if(setFunc) {
this.emit(`${setFunc}`);
}
if (argHasThrowFunc && argHasThrowFunc.get(i)) {
this.emit(argHasThrowFunc.get(i));
} else {
this.visitExpr(expr, level, env, { pointer: true });
}
if(setFunc) {
this.emit(')');
}
}
}
if (i !== ast.args.length - 1) {
this.emit(', ');
}
}
this.emit(')');
}
visitPropertyAccess(ast, level, env, expected) {
assert.ok(ast.type === 'property_access' || ast.type === 'property');
var id = _name(ast.id);
if(ast.id.tag === Tag.VID){
id = _vid(ast.id);
}
var expr = '';
if (id === '__response') {
expr += RESPONSE;
} else if (id === '__request') {
expr += REQUEST;
} else {
expr += _avoidReserveName(id);
}
var fieldType = '';
var current = ast.id.inferred;
var isMap = '';
for (var i = 0; i < ast.propertyPath.length; i++) {
const name = _name(ast.propertyPath[i]);
if (current.type === 'model') {
if (ast.inferred.type === 'array') {
fieldType = `[]${this.getType(_name(ast.inferred.itemType), false)}`;
} else {
fieldType = ast.propertyPathTypes[i].name;
}
expr += `.${_format(name)}`;
} else {
fieldType = ast.propertyPathTypes[i].name;
expr += `["${name}"]`;
}
if (current.type === 'map') {
isMap = true;
}
current = ast.propertyPathTypes[i];
}
if (expected && expected.needCast === 'false') {
this.emit(expr);
} else if (expected && expected.pointer) {
if (ast.id.inferred.type !== 'model' && !isMap && (DSL.util.isBasicType(fieldType) ||
(current.type === 'array' && current.itemType.type === 'basic')) && !_isFilterType(fieldType)) {
this.emit(`${_setExtendFunc(fieldType)}${expr})`);
} else {
this.emit(`${expr}`);
}
} else if ((ast.id.inferred.type === 'model' || isMap) && (DSL.util.isBasicType(fieldType) ||
(current.type === 'array' && current.itemType.type === 'basic')) && !_isFilterType(fieldType)) {
this.emit(`${_setValueFunc(fieldType)}${expr})`);
} else {
this.emit(expr);
}
}
visitExpr(ast, level, env, expected, argHasThrowFunc) {
var isPointer = false;
env = env || {
pointerParams: [],
local: new Map()
};
if (ast.type === 'boolean') {
if (expected && expected.pointer) {
this.emit(`dara.Bool(${ast.value})`);
} else {
this.emit(ast.value);
}
} else if (ast.type === 'null') {
this.emit('nil');
} else if (ast.type === 'property_access') {
this.visitPropertyAccess(ast, level, env, expected);
} else if (ast.type === 'string') {
if (expected && expected.pointer) {
this.emit(`dara.String("${_string(ast.value).replace(/"/g, '\\"')}")`);
} else {
this.emit(`"${_string(ast.value).replace(/"/g, '\\"')}"`);
}
} else if (ast.type === 'number') {
if (expected && expected.pointer) {
if (_name(expected) && _name(expected) !== 'any') {
this.emit(`${_setExtendFunc(_name(expected))}${ast.value.value})`);
} else if (_name(expected) === 'any') {
this.emit(`${_setExtendFunc(ast.value.type)}${ast.value.value})`);
} else {
this.emit(`dara.Int(${ast.value.value})`);
}
} else {
this.emit(ast.value.value);
}
} else if (ast.type === 'object') {
this.visitObject(ast, level, env, expected);
} else if (ast.type === 'variable') {
isPointer = false;
var id = _name(ast.id);
if ((env.pointerParams && !env.pointerParams.includes(id)) && (expected && expected.pointer) && (ast.inferred.type !== 'model' &&
((DSL.util.isBasicType(_name(ast.inferred)) && !_isFilterType(_name(ast.inferred))) &&
!(ast.inferred.type === 'array' && ast.inferred.itemType.type === 'basic')))) {
isPointer = true;
this.emit(_setExtendFunc(_name(ast.inferred)));
}
if((env.pointerParams && env.pointerParams.includes(id)) && (!expected || !expected.pointer) &&
((ast.inferred.type === 'array' && ast.inferred.itemType.type === 'basic') ||
(DSL.util.isBasicType(_name(ast.inferred)) && !_isFilterType(_name(ast.inferred))))) {
isPointer = _setValueFunc(_name(ast.inferred)) ? true : false;
this.emit(_setValueFunc(_name(ast.inferred)));
}
if (id === '__response') {
this.emit(RESPONSE);
} else if (id === '__request') {
this.emit(REQUEST);
} else if (ast.inferred && _name(ast.inferred) === 'class') {
this.emit('new(' + _avoidReserveName(id) + ')');
} else {
this.emit(_avoidReserveName(id));
}
if (isPointer) {
this.emit(')');
}
} else if (ast.type === 'virtualVariable') {
if ((!expected || !expected.pointer) && ((ast.inferred.type === 'array' &&
ast.inferred.itemType.type === 'basic') || (DSL.util.isBasicType(_name(ast.inferred))
&& !_isFilterType(_name(ast.inferred))))) {
this.emit(`${_setValueFunc(_name(ast.inferred))}${_vid((_name(ast.vid)))})`);
} else {
this.emit(`${_vid(_avoidVariableKeywords(_format(_name(ast.vid))))}`);
}
} else if (ast.type === 'decrement') {
if (ast.position === 'front') {
this.emit('--');
}
this.visitExpr(ast.expr, level, env, { pointer: false }, argHasThrowFunc);
if (ast.position === 'backend') {
this.emit('--');
}
} else if (ast.type === 'increment') {
if (ast.position === 'front') {
this.emit('++');
}
this.visitExpr(ast.expr, level, env, { pointer: false }, argHasThrowFunc);
if (ast.position === 'backend') {
this.emit('++');
}
} else if (ast.type === 'template_string') {
var j = 0;
if (expected && expected.pointer) {
this.emit(`dara.String(`);
}
for (let i = 0; i < ast.elements.length; i++) {
var item = ast.elements[i];
if (item.type === 'element' && _string(item.value) === '') {
continue;
}
if (j > 0) {
this.emit(' + ');
}
j = j + 1;
if (item.type === 'element') {
let val = _string(item.value);
val = val.replace(/[\n]/g, '" + \n"');
this.emit(`"${val}"`);
} else if (item.type === 'expr') {
const expr = item.expr;
if (expr.inferred && _name(expr.inferred) !== 'string') {
this.emit(`dara.ToString(`);
this.visitExpr(expr, level, env);
this.emit(`)`);
} else {
let pointer = false;
if(expr.type === 'call' && this.isNotBuiltin(expr)) {
pointer = _setValueFunc(_name(ast.inferred)) ? true : false;
this.emit(`${_setValueFunc(_name(ast.inferred))}`);
}
this.visitExpr(expr, level, env);
if(pointer) {
this.emit(')');
}
}
} else {
throw new Error('unimpelemented');
}
}
if (expected && expected.pointer) {
this.emit(`)`);
}
} else if (ast.type === 'call') {
if(_isIterator(ast.inferred)) {
env.yieldFunc ? this.emit('', level) : this.emit('go ', level);
}
this.visitCall(ast, level, env, argHasThrowFunc);
} else if (ast.type === 'group') {
this.emit('(');
this.visitExpr(ast.expr, level, env, expected, argHasThrowFunc);
this.emit(')');
} else if (_isBinaryOp(ast.type)) {
if ((env.pointerParams && env.pointerParams.includes(id)) || (expected && expected.pointer)) {
this.emit(`${_setExtendFunc(_name(ast.inferred))}`);
}
this.visitExpr(ast.left, level, env, { pointer: false }, argHasThrowFunc);
if (ast.type === 'or') {
this.emit(' || ');
} else if (ast.type === 'add') {
this.emit(' + ');
} else if (ast.type === 'subtract') {
this.emit(' - ');
} else if (ast.type === 'div') {
this.emit(' / ');
} else if (ast.type === 'multi') {
this.emit(' * ');
} else if (ast.type === 'and') {
this.emit(' && ');
} else if (ast.type === 'or') {
this.emit(' || ');
} else if (ast.type === 'lte') {
this.emit(' <= ');
} else if (ast.type === 'lt') {
this.emit(' < ');
} else if (ast.type === 'gte') {
this.emit(' >= ');
} else if (ast.type === 'gt') {
this.emit(' > ');
} else if (ast.type === 'neq') {
this.emit(' != ');
} else if (ast.type === 'eq') {
this.emit(' == ');
}
this.visitExpr(ast.right, level, env, { pointer: false }, argHasThrowFunc);
if (expected && expected.pointer) {
this.emit(')');
}
} else if (ast.type === 'construct') {
this.visitConstruct(ast, level, env);
} else if (ast.type === 'construct_model') {
this.visitConstructModel(ast, level, env);
} else if (ast.type === 'not') {
this.emit(`!`);
this.visitExpr(ast.expr, level, env, expected, argHasThrowFunc);
} else if (ast.type === 'array') {
this.visitArray(ast, level, env, expected);
} else if (ast.type === 'map_access') {
this.visitMapAccess(ast, level, env, expected);
} else if (ast.type === 'array_access') {
this.visitArrayAccess(ast, level, env, expected);
} else if (ast.type === 'super') {
this.visitSuper(ast, level, env);
} else {
throw new Error('unimpelemented');
}
}
visitYieldArgs(ast, level, env) {
let pointer = false;
let setFunc;
var hasThrowCall = (ast.expr.type === 'call' &&
ast.expr.hasThrow) || ast.expr.type === 'construct';
if (ast.left.type === 'property_assign' || ast.left.type === 'property') {
pointer = true;
this.visitPropertyAccess(ast.left, level, env, { needCast: 'false', type: 'pointer', pointer });
} else if (ast.left.type === 'variable') {
const name = _name(ast.left.id);
pointer = env.pointerParams && env.pointerParams.includes(name);
this.emit(`${name}`);
if(hasThrowCall){
let dealFunc = this.getVarDealFunc(ast.expr, { pointer });
setFunc = dealFunc && dealFunc(_name(ast.expr.inferred));
if(setFunc) {
this.emit('Tmp');
}
}
} else if (ast.left.type === 'virtualVariable') {
pointer = true;
this.emit(`${_vid(_avoidVariableKeywords(_format(_name(ast.left.vid))))}`, level);
} else if (ast.left.type === 'map_access') {
pointer = true;
this.visitMapAccess(ast.left, level, env, { pointer });
} else if (ast.left.type === 'array_access') {
pointer = true;
this.visitArrayAccess(ast.left, level, env, { pointer });
} else {
throw new Error('unimpelemented');
}
if (hasThrowCall) {
this.emit(', _yieldErr');
}
}
visitSuper(ast, level, env) {
assert.equal(ast.type, 'super');
this.emit(`_err = client.${this.structName}.Init(`);
for (let i = 0; i < ast.args.length; i++) {
this.visitExpr(ast.args[i], level, env, { pointer: true });
}
this.emit(`)\n`, level);
if(env.runtimeBody){
this.visitAPIErrCatch(level, env);
} else {
this.emit(`if _err != nil {\n`, level);
this.emit(`return _err\n`, level + 1);
this.emit(`}`, level);
}
}
visitMapAccess(ast, level, env, expected) {
assert.equal(ast.type, 'map_access');
let mapName = _name(ast.id);
if (ast.id.tag === Tag.VID) {
mapName = _vid(mapName);
}
if (ast.propertyPath && ast.propertyPath.length) {
var current = ast.id.inferred;
for (var i = 0; i < ast.propertyPath.length; i++) {
var name = _name(ast.propertyPath[i]);
if (current.type === 'model') {
mapName += `.${_format(name)}`;
} else {
mapName += `["${name}"]`;
}
current = ast.propertyPathTypes[i];
}
}
let accessKey = ast.accessKey;
if (!DSL.util.isBasicType(ast.accessKey.type) && !ast.accessKey.inferred) {
ast.accessKey.inferred = {
type: 'basic',
name: 'string'
};
}
if ((!expected || !expected.pointer) && (DSL.util.isBasicType(_name(ast.inferred)) &&
!_isFilterType(_name(ast.inferred))) && _name(ast.inferred) !== 'model') {
this.emit(`${_setValueFunc(_name(ast.inferred))}${mapName}[`);
this.visitExpr(accessKey, level, env);
this.emit(`])`);
} else {
this.emit(`${mapName}[`);
if (ast.propertyPathTypes && ast.propertyPathTypes.length) {
if (ast.propertyPathTypes[ast.propertyPathTypes.length - 1].type === 'map') {
accessKey = {
inferred: ast.propertyPathTypes[ast.propertyPathTypes.length - 1].keyType,
...accessKey
};
}
}
this.visitExpr(accessKey, level, env);
this.emit(`]`);
}
}
visitArrayAccess(ast, level, env, expected) {
assert.equal(ast.type, 'array_access');
let arrayName = _name(ast.id);
if (ast.id.tag === DSL.Tag.Tag.VID) {
arrayName = _vid(arrayName);
}
if (ast.propertyPath && ast.propertyPath.length) {
var current = ast.id.inferred;
for (var i = 0; i < ast.propertyPath.length; i++) {
var name = _name(ast.propertyPath[i]);
if (current.type === 'model') {
arrayName += `.${_format(name)}`;
} else {
arrayName += `["${name}"]`;
}
current = ast.propertyPathTypes[i];
}
}
if ((!expected || !expected.pointer) && (DSL.util.isBasicType(_name(ast.inferred)) &&
!_isFilterType(_name(ast.inferred))) && _name(ast.inferred) !== 'model') {
this.emit(`${_setValueFunc(_name(ast.inferred))}${arrayName}[`);
this.visitExpr(ast.accessKey, level, env);
this.emit(`])`);
} else {
this.emit(`${arrayName}[`);
this.visitExpr(ast.accessKey, level, env);
this.emit(`]`);
}
}
visitArray(ast, level, env, expected) {
assert.equal(ast.type, 'array');
let expectedType;
if (!expected || !expected.type || (expected && _name(expected) && _name(expected) === 'any')) {
expectedType = ast.inferred;
} else {
expectedType = expected;
}
this.visitPointerType(expectedType, level, env);
let arrayComments = DSL.comment.getBetweenComments(this.comments, ast.tokenRange[0], ast.tokenRange[1]);
if (ast.items.length === 0) {
this.emit(`{`);
if (arrayComments.length > 0) {
this.emit('\n');
this.visitComments(arrayComments, level + 1);
this.emit('', level);
}
this.emit('}');
return;
}
let item;
this.emit(`{`);
let itemType = expectedType.subType || expectedType.itemType;
itemType.pointer = true;
for (let i = 0; i < ast.items.length; i++) {
item = ast.items[i];
let comments = DSL.comment.getFrontComments(this.comments, item.tokenRange[0]);
if (comments.length > 0) {
this.emit('\n');
this.visitComments(comments, level + 1);
this.emit('', level + 1);
}
this.visitExpr(item, level + 1, env, itemType);
if (i < ast.items.length - 1) {
this.emit(`, `);
}
}
if (item) {
//find the last item's back comment
let comments = DSL.comment.getBetweenComments(this.comments, item.tokenRange[0], ast.tokenRange[1]);
if (comments.length > 0) {
this.emit('\n');
this.visitComments(comments, level + 1);
this.emit(`}`, level);
return;
}
}
this.emit(`}`);
}
visitConstruct(ast, level, env) {
assert.equal(ast.type, 'construct');
let moudleName = _name(ast.aliasId);
let constructFunc = this.constructFunc[moudleName];
if(moudleName.startsWith('$') && this.builtin[moudleName]) {
constructFunc = moudleName.replace('$', '');
moudleName = this.getType(moudleName, false);
this.emit(moudleName);
} else {
this.visitModuleName(ast.aliasId);
}
this.emit(`.New${constructFunc}(`);
for (let i = 0; i < ast.args.length; i++) {
this.visitExpr(ast.args[i], level, env);
}
this.emit(`)`);
}
visitConstructModelField(ast, level, env) {
var str = '';
var expected = ast.expectedType;
expected.pointer = true;
if (ast.expr.type === 'property_access') {
expected.needCast = 'false';
}
let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]);
this.visitComments(comments, level);
this.emit(`${_format(_name(ast.fieldName))}: ${str}`, level);
if ((_name(ast.expectedType) === 'number' || _name(ast.expectedType) === 'integer') && _name(ast.expr.inferred) === 'int32') {
this.emit(`dara.ToInt(`);
this.visitExpr(ast.expr, level, env, expected);
this.emit(`)`);
} else if (ast.expr.type !== 'number' && (_name(ast.expr.inferred) === 'number' || _name(ast.expr.inferred) === 'integer') && _name(ast.expectedType) === 'int32') {
this.emit(`dara.ToInt32(`);
this.visitExpr(ast.expr, level, env, expected);
this.emit(`)`);
} else {
let setFunc;
if(ast.expr && ast.expr.type === 'call') {
let dealFunc = this.getVarDealFunc(ast.expr, expected);
setFunc = dealFunc && dealFunc(_name(ast.expr.inferred));
}
if(setFunc) {
this.emit(`${setFunc}`);
}
this.visitExpr(ast.expr, level, env, expected);
if(setFunc) {
this.emit(')');
}
}
if (str !== '') {
this.emit(`)`);
}
this.emit(`,\n`);
}
visitConstructModel(ast, level, env) {
assert.equal(ast.type, 'construct_model');
let modelName = '';
this.emit('&');
if ((ast.inferred && ast.inferred.moduleName) || ast.aliasId.isModule) {
this.visitModuleName(ast.aliasId);
for (let i = 0; i < ast.propertyPath.length; i++) {
const item = ast.propertyPath[i];
modelName += _format(_name(item));
}
const externEx = this.usedExternException.get(_name(ast.aliasId));
if (externEx && externEx.has(modelName)) {
modelName += 'Error';
}
modelName = `.${modelName}`;
} else {
modelName = _modelName(_format(_name(ast.aliasId)));
if (this.predefined[modelName] && this.predefined[modelName].isException) {
modelName += 'Error';
}
}
if (ast.object && ast.object.fields.length > 0) {
const fields = ast.object.fields;
this.emit(`${modelName}{\n`);
for (let i = 0; i < fields.length; i++) {
const field = fields[i];
this.visitConstructModelField(field, level + 1, env);
}
this.emit(`}`, level);
} else {
this.emit(`${modelName}{`);
let comments = DSL.comment.getBetweenComments(this.comments, ast.tokenRange[0], ast.tokenRange[1]);
if (comments.length > 0) {
this.emit('\n');
this.visitComments(comments, level + 1);
this.emit('', level);
}
this.emit(`}`);
}
}
visitYield(ast, level, env) {
assert.equal(ast.type, 'yield');
// TODO
if (ast.expr.type === 'null' || ast.expr.type === 'variable' ||
ast.expr.type === 'property_access' || ast.expr.type === 'virtualVariable' ||
ast.expr.type === 'decrement' || ast.expr.type === 'increment' || ast.expr.type === 'construct_model' ||
ast.expr.type === 'group' || _isBinaryOp(ast.expr.type)) {
this.emit(`_yield <- `, level);
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit(`\n`);
return;
}
var returnType = '';
if (env.returnType.idType === 'module') {
this.emit(`yield <- ${this.clientName[_name(env.returnType)].replace('*', '&')}{}\n`, level);
} else if (_name(env.returnType) && !(DSL.util.isBasicType(_name(env.returnType)) && !_isFilterType(_name(env.returnType)))
&& env.returnType.idType !== 'typedef') {
this.emit(`yield <- ${_initValue(_name(env.returnType))}\n`, level);
} else if (env.returnType.path && env.returnType.type !== 'moduleTypedef') {
for (let i = 0; i < env.returnType.path.length; i++) {
const path = env.returnType.path[i];
if (i === 0) {
returnType += _name(path).toLowerCase();
} else {
returnType += '.' + _name(path);
}
}
this.emit(`yield <- ${_initValue(returnType)}\n`, level);
} else if (env.returnType.type === 'map') {
this.emit(`yield <- make(`, level);
this.visitPointerType(env.returnType, level, env);
this.emit(`)\n`);
} else if (env.returnType.type === 'array') {
this.emit(`yield <- make(`, level);
this.visitPointerType(env.returnType, level, env);
this.emit(`, 0)\n`);
}
if (ast.expr.type === 'call') {
var argHasThrowFunc = this.visitFunctionNested(ast.expr, level, env);
var hasThrow = false;
hasThrow = ast.expr.hasThrow;
if (hasThrow) {
this.emit(`_body, _err := `, level);
} else {
this.emit(`_body := `, level);
}
this.visitExpr(ast.expr, level, env, { pointer: true }, argHasThrowFunc);
this.emit(`\n`);
if (hasThrow) {
this.emit(`if _err != nil {\n`, level);
this.emit(`_yieldErr <- _err\n`, level + 1);
this.emit(`return\n`, level + 1);
this.emit(`}\n`, level);
}
if (!ast.needCast) {
this.emit(`yield <- _body\n`, level);
} else {
if (env.hasThrow) {
this.emit(`_err := dara.ConvertChan(_body, _yield)\n`, level);
this.emit(`if _err != nil {\n`, level);
this.emit(`_yieldErr <- _err\n`, level + 1);
this.emit(`return\n`, level + 1);
this.emit(`}\n`, level);
} else {
this.emit(`dara.ConvertChan(_body, _yield)\n`, level);
}
}
} else if (ast.expr.type === 'template_string') {
this.emit(`yield <- `, level);
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit(`\n`);
} else if (ast.expr.fields) {
// for (let i = 0; i < ast.expr.fields.length; i++) {
// const field = ast.expr.fields[i];
// if (field.expr.inferred && _name(field.expr.inferred) === 'readable') {
// this.emit(
// `_result.${_format(_name(field.fieldName))} = `,
// level
// );
// this.visitExpr(field.expr);
// this.emit(`\n`);
// delete ast.expr.fields[i];
// }
// }
if (env.hasThrow) {
this.emit(`_err := dara.ConvertChan(`, level);
} else {
this.emit(`dara.ConvertChan(`, level);
}
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit(`, _yield)\n`);
if (env.hasThrow) {
this.emit(`if _err != nil {\n`, level);
this.emit(`_yieldErr <- _err\n`, level + 1);
this.emit(`return\n`, level + 1);
this.emit(`}\n`, level);
}
} else if (ast.expr.items) {
if (env.hasThrow) {
this.emit(`_err := dara.ConvertChan(`, level);
} else {
this.emit(`dara.ConvertChan(`, level);
}
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit(`, _yield)\n`);
if (env.hasThrow) {
this.emit(`if _err != nil {\n`, level);
this.emit(`_yieldErr <- _err\n`, level + 1);
this.emit(`return\n`, level + 1);
this.emit(`}\n`, level);
}
} else if (ast.expr.type === 'construct') {
this.emit(`_result, _err := `, level);
this.visitConstruct(ast.expr, level, env);
this.emit(`\n`);
this.emit(`if _err != nil {\n`, level);
this.emit(`_yieldErr <- _err\n`, level + 1);
this.emit(`return\n`, level + 1);
this.emit(`}\n`, level);
} else if (ast.expr.type === 'map_access') {
this.emit(`yield <- `, level);
this.visitMapAccess(ast.expr, level, env, { pointer: true });
this.emit(`\n`);
} else if (DSL.util.isBasicType(ast.expr.type)) {
this.emit(`yield <- `, level);
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit(`\n`);
}
}
visitReturn(ast, level, env) {
assert.equal(ast.type, 'return');
if(env.yieldErrDeal) {
this.emit(`_err = <- _yieldErr\n`, level);
}
if (!ast.expr && !env.finallyBlock) {
if (env.hasThrow && env.runtimeBody) {
this.emit(`return _err\n`, level);
} else {
this.emit(`return\n`, level);
}
return;
}
if(_name(env.returnType) === 'void') {
if (env.hasThrow && env.runtimeBody) {
this.emit(`return _err\n`, level);
} else {
this.emit(`return\n`, level);
}
return;
}
if (ast.expr.type === 'null' || ast.expr.type === 'variable' ||
ast.expr.type === 'property_access' || ast.expr.type === 'virtualVariable' ||
ast.expr.type === 'decrement' || ast.expr.type === 'increment' ||
ast.expr.type === 'group' || _isBinaryOp(ast.expr.type)) {
this.emit(`_result = `, level);
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit(`\n`);
if(!env.finallyBlock) {
if (env.hasThrow) {
this.emit(`return _result , _err`, level);
} else {
this.emit(`return _result`, level);
}
this.emit(`\n`);
env.hasReturn = false;
}
return;
}
// if (env.returnType.idType === 'module') {
// this.emit(`_result = ${this.clientName[_name(env.returnType)].replace('*', '&')}{}\n`, level);
// } else if (_name(env.returnType) && !(DSL.util.isBasicType(_name(env.returnType)) && !_isFilterType(_name(env.returnType)))
// && env.returnType.idType !== 'typedef') {
// this.emit(`_result = ${_initValue(_name(env.returnType))}\n`, level);
// } else if (env.returnType.path && env.returnType.type !== 'moduleTypedef') {
// for (let i = 0; i < env.returnType.path.length; i++) {
// const path = env.returnType.path[i];
// if (i === 0) {
// returnType += _name(path).toLowerCase();
// } else {
// returnType += '.' + _name(path);
// }
// }
// this.emit(`_result = ${_initValue(returnType)}\n`, level);
// } else if (env.returnType.type === 'map') {
// this.emit(`_result = make(`, level);
// this.visitPointerType(env.returnType, level, env);
// this.emit(`)\n`);
// } else if (env.returnType.type === 'array') {
// this.emit(`_result = make(`, level);
// this.visitPointerType(env.returnType, level, env);
// this.emit(`, 0)\n`);
// }
if (ast.expr.type === 'call') {
var argHasThrowFunc = this.visitFunctionNested(ast.expr, level, env);
var hasThrow = false;
hasThrow = ast.expr.hasThrow;
if (hasThrow) {
this.emit(`_body, _err := `, level);
} else {
this.emit(`_body := `, level);
}
this.visitExpr(ast.expr, level, env, { pointer: true }, argHasThrowFunc);
this.emit(`\n`);
if (hasThrow) {
this.emit(`if _err != nil {\n`, level);
this.emit(`return _result, _err\n`, level + 1);
this.emit(`}\n`, level);
}
if (!ast.needCast) {
this.emit(`_result = _body\n`, level);
} else {
if (env.hasThrow) {
this.emit(`_err = dara.Convert(_body, &_result)\n`, level);
} else {
this.emit(`dara.Convert(_body, &_result)\n`, level);
}
}
} else if (ast.expr.type === 'template_string') {
this.emit(`_result = `, level);
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit(`\n`);
} else if (ast.expr.fields) {
for (let i = 0; i < ast.expr.fields.length; i++) {
const field = ast.expr.fields[i];
if (field.expr.inferred && _name(field.expr.inferred) === 'readable') {
this.emit(`_result.${_format(_name(field.fieldName))} = `, level);
this.visitExpr(field.expr);
this.emit(`\n`);
delete ast.expr.fields[i];
}
}
if (env.hasThrow) {
this.emit(`_err = dara.Convert(`, level);
} else {
this.emit(`dara.Convert(`, level);
}
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit(`, &_result)\n`);
this.emit('\n');
} else if (ast.expr.items) {
this.emit('_result = ', level);
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit('\n');
} else if (ast.expr.type === 'construct') {
this.emit(`_result, _err = `, level);
this.visitConstruct(ast.expr, level, env);
this.emit(`\n`);
} else if (ast.expr.type === 'map_access') {
this.emit(`_result = `, level);
this.visitMapAccess(ast.expr, level, env, { pointer: true });
this.emit(`\n`);
} else if (DSL.util.isBasicType(ast.expr.type)) {
this.emit(`_result = `, level);
this.visitExpr(ast.expr, level, env, { pointer: true });
this.emit(`\n`);
}
if(!env.finallyBlock) {
if (env.hasThrow) {
this.emit(`return _result, _err\n`, level);
} else {
this.emit(`return _result\n`, level);
}
env.hasReturn = false;
}
}
visitRetry(ast, level) {
assert.equal(ast.type, 'retry');
this.emit(`\n`);
}
visitIf(ast, level, env) {
assert.equal(ast.type, 'if');
let argHasThrowFunc;
if (ast.condition.type === 'not' && ast.condition.expr && ast.condition.expr.type === 'call') {
argHasThrowFunc = this.visitFunctionNested(ast.condition.expr, level, env);
} else if (ast.condition.type === 'call') {
argHasThrowFunc = this.visitFunctionNested(ast.condition, level, env);
}
this.emit('if ', level);
let setFunc;
if(ast.condition && ast.condition.type === 'call') {
let dealFunc = this.getVarDealFunc(ast.condition, true);
setFunc = dealFunc && dealFunc(_name(ast.condition.inferred));
}
if(setFunc) {
this.emit(`${setFunc}`);
}
this.visitExpr(ast.condition, level + 1, env, false, argHasThrowFunc);
if(setFunc) {
this.emit(')');
}
this.emit(' {\n');
this.visitStmts(ast.stmts, level + 1, env);
if (ast.elseIfs) {
for (let i = 0; i < ast.elseIfs.length; i++) {
let elseIf = ast.elseIfs[i];
this.emit(`} else if `, level);
let setFunc;
if(elseIf.condition && elseIf.condition.type === 'call') {
let dealFunc = this.getVarDealFunc(elseIf.condition, true);
setFunc = dealFunc && dealFunc(_name(elseIf.condition.inferred));
}
if(setFunc) {
this.emit(`${setFunc}`);
}
this.visitExpr(elseIf.condition, level + 1, env, false, argHasThrowFunc);
if(setFunc) {
this.emit(')');
}
this.emit(' {\n');
this.visitStmts(elseIf.stmts, level + 1, env);
}
}
if (ast.elseStmts) {
this.emit(`} else {\n`, level);
this.visitStmts(ast.elseStmts, level + 1, env);
}
this.emit('}\n\n', level);
}
visitThrow(ast, level, env) {
if (ast.expr.type === 'construct_model') {
this.emit(`_err ${env.yieldFunc ? ':' : ''}= `, level);
this.visitConstructModel(ast.expr, level, env);
this.emit('\n');
} else {
this.emit(`_err ${env.yieldFunc ? ':' : ''}= dara.NewSDKError(`, level);
this.visitObject(ast.expr, level, env, 'map[string]interface{}');
this.emit(')\n');
}
if(env.runtimeBody && !this.noCompatible) {
this.emit(`if dara.BoolValue(client.DisableSDKError) != true {\n`, level);
this.emit(`_err = dara.TeaSDKError(_err)\n`, level + 1);
this.emit(`}\n`, level);
}
if(env.try) {
const tryStmt = env.try;
env.try = null;
this.visitCatch(tryStmt, level, env);
} else if (!env.returnType) {
this.emit(`return _err\n`, level);
} else if (_name(env.returnType) === 'void') {
if(env.yieldFunc){
this.emit(`_yieldErr <- _err\n`, level);
this.emit(`return\n`, level);
} else if (env.hasThrow) {
this.emit(`return _err\n`, level);
} else {
this.emit(`return\n`, level);
}
} else {
this.emit(`return _result, _err\n`, level);
}
}
visitAssign(ast, level, env) {
assert.equal(ast.type, 'assign');
let pointer = false;
let newVar = false;
let setFunc;
var hasThrowCall = (ast.expr.type === 'call' &&
ast.expr.hasThrow) || ast.expr.type === 'construct';
const yieldAssign = _isIterator(ast.expr.inferred) && ast.expr.type === 'call';
env.yieldErrDeal = yieldAssign && !env.yieldFunc;
if(yieldAssign) {
ast.expr.args.push({
yieldArg: true,
ast,
});
} else {
if (ast.left.type === 'property_assign' || ast.left.type === 'property') {
this.emit(``, level);
pointer = true;
this.visitPropertyAccess(ast.left, level, env, { needCast: 'false', type: 'pointer', pointer });
} else if (ast.left.type === 'variable') {
const name = _name(ast.left.id);
pointer = env.pointerParams && env.pointerParams.includes(name);
this.emit(`${name}`, level);
if(hasThrowCall){
let dealFunc = this.getVarDealFunc(ast.expr, { pointer });
setFunc = dealFunc && dealFunc(_name(ast.expr.inferred));
if(setFunc) {
this.emit('Tmp');
if(!env || !env.local || !env.local.has(`${name}Tmp`)) {
newVar = true;
}
}
}
} else if (ast.left.type === 'virtualVariable') {
pointer = true;
this.emit(`${_vid(_avoidVariableKeywords(_format(_name(ast.left.vid))))}`, level);
} else if (ast.left.type === 'map_access') {
this.emit(``, level);
pointer = true;
this.visitMapAccess(ast.left, level, env, { pointer });
} else if (ast.left.type === 'array_access') {
this.emit(``, level);
pointer = true;
this.visitArrayAccess(ast.left, level, env, { pointer });
} else {
throw new Error('unimpelemented');
}
if (hasThrowCall) {
this.emit(`, _err ${newVar ? ':' : ''}= `);
} else {
this.emit(` = `);
}
}
if (ast.expr.needToReadable) {
this.emit(`dara.ToReader(`);
this.visitExpr(ast.expr, level, env, { needCast: 'false', pointer: true });
this.emit(`)`);
} else if (ast.expr.type === 'object' && ast.left.inferred &&
ast.left.inferred.type === 'map' &&
_name(ast.left.inferred.valueType) === 'any') {
this.visitObject(ast.expr, level, env, 'map[string]interface{}');
} else {
if ((ast.left.inferred.name === 'number' || ast.left.inferred.name === 'integer') && ast.expr.inferred.name === 'int32') {
this.emit(`${pointer ? 'dara.ToInt(' : 'int('}`);
this.visitExpr(ast.expr, level, env, { pointer });
this.emit(`)`);
} else if (ast.expr.type !== 'number' && (ast.expr.inferred.name === 'number' || ast.expr.inferred.name === 'integer') && ast.left.inferred.name === 'int32') {
this.emit(`${pointer ? 'dara.ToInt32(' : 'int32('}`);
this.visitExpr(ast.expr, level, env, { pointer });
this.emit(`)`);
} else if(ast.expr.type === 'call'){
if(hasThrowCall && ast.left.type === 'variable') {
this.visitExpr(ast.expr, level, env, { pointer });
if(setFunc) {
this.emit('\n');
let name = _name(ast.left.id);
this.emit(`${name} = ${setFunc}${name}Tmp)`, level);
}
} else {
let dealFunc = this.getVarDealFunc(ast.expr, { pointer });
setFunc = dealFunc && dealFunc(_name(ast.expr.inferred));
if(setFunc) {
this.emit(`${setFunc}`);
}
this.visitExpr(ast.expr, level, env, { pointer });
if(setFunc) {
this.emit(')');
}
}
} else {
this.visitExpr(ast.expr, level, env, { pointer });
}
}
this.emit(`\n`);
if (hasThrowCall && !yieldAssign) {
if(env.runtimeBody){
this.visitAPIErrCatch(level, env);
} else if(env.yieldFunc) {
this.emit(`if _err != nil {\n`, level);
this.emit(`_yieldErr <- _err\n`, level + 1);
this.emit(`}\n\n`, level);
} else if (env.returnType && _name(env.returnType) !== 'void') {
this.emit(`if _err != nil {\n`, level);
this.emit(`return _result, _err\n`, level + 1);
this.emit(`}\n\n`, level);
} else {
this.emit(`if _err != nil {\n`, level);
this.emit(`return _err\n`, level + 1);
this.emit(`}\n\n`, level);
}
}
}
getVarDealFunc(ast, expected) {
if((!expected || !expected.pointer) && (DSL.util.isBasicType(_name(ast.inferred))
&& !_isFilterType(_name(ast.inferred))) && this.isNotBuiltin(ast)) {
return _setValueFunc;
}
if(expected && expected.pointer && !this.isNotBuiltin(ast) && !_isFilterType(_name(ast.inferred))) {
return _setExtendFunc;
}
}
visitDeclare(ast, level, env) {
var id = _name(ast.id);
var expected = ast.expectedType || {};
const expr = ast.expr;
let varDealFunc, setFunc;
if (expr.type === 'call') {
var argHasThrowFunc = this.visitFunctionNested(expr, level, env);
}
var hasThrowCall = (expr.type === 'call' &&
expr.hasThrow) || expr.type === 'construct';
const yieldAssign = _isIterator(ast.expr.inferred) && ast.expr.type === 'call';
env.yieldErrDeal = yieldAssign && !env.yieldFunc;
if(yieldAssign) {
this.emit(`${id} := make(chan `, level);
this.visitType(ast.expr.inferred.valueType, level);
this.emit(', 1)\n');
if((!env.hasThrow && hasThrowCall) || (env.hasThrow && !env.yieldFunc)) {
this.emit(`_yieldErr := make(chan error, 1)\n`, level);
}
ast.expr.args.push({
yieldArg: true,
ast: {
left: {
type: 'variable',
id: ast.id
},
expr: ast.expr,
},
});
} else {
if (hasThrowCall) {
varDealFunc = this.getVarDealFunc(expr, expected);
let tmpName = id;
if (varDealFunc) {
tmpName = tmpName + 'Tmp';
}
if(env && env.local) {
env.local.set(tmpName, true);
}
this.emit(`${tmpName}, _err := `, level);
} else if (expr.type === 'null') {
this.emit(`var ${id} `, level);
this.visitType(ast.expectedType, level);
this.emit('\n');
return;
} else {
this.emit(`${id} := `, level);
if(expr && expr.type === 'call') {
let dealFunc = this.getVarDealFunc(expr, expected);
setFunc = dealFunc && dealFunc(_name(expr.inferred));
}
if(setFunc) {
this.emit(`${setFunc}`);
}
}
}
expected.pointer = false;
if (ast.expectedType) {
if ((_name(ast.expectedType) === 'number' || _name(ast.expectedType) === 'integer') && expr.inferred.name === 'int32') {
this.emit(`int(`);
this.visitExpr(expr, level, env, expected, argHasThrowFunc);
this.emit(`)`);
} else if (
(expr.type !== 'number' && (expr.inferred.name === 'number' || expr.inferred.name === 'integer')
|| expr.type === 'number') && _name(ast.expectedType) === 'int32') {
this.emit(`int32(`);
this.visitExpr(expr, level, env, expected, argHasThrowFunc);
this.emit(`)`);
} else {
this.visitExpr(expr, level, env, expected, argHasThrowFunc);
}
} else {
this.visitExpr(expr, level, env, expected, argHasThrowFunc);
if(setFunc) {
this.emit(')');
}
}
this.emit('\n');
if(varDealFunc) {
this.emit(`${id} := `, level);
this.emit(`${varDealFunc(_name(expr.inferred))}`);
this.emit(`${id}Tmp)\n`);
}
if (hasThrowCall && !yieldAssign) {
if(env.runtimeBody){
this.visitAPIErrCatch(level, env);
} else if(env.yieldFunc) {
this.emit(`if _err != nil {\n`, level);
this.emit(`_yieldErr <- _err\n`, level + 1);
this.emit(`return\n`, level + 1);
this.emit(`}\n\n`, level);
} else if(env.try) {
const tryStmt = env.try;
env.try = null;
this.visitCatch(tryStmt, level, env);
} else if (env.returnType && _name(env.returnType) !== 'void') {
this.emit(`if _err != nil {\n`, level);
this.emit(`return _result, _err\n`, level + 1);
this.emit(`}\n\n`, level);
} else {
this.emit(`if _err != nil {\n`, level);
this.emit(`return _err\n`, level + 1);
this.emit(`}\n\n`, level);
}
}
}
visitStmts(ast, level, env) {
assert.equal(ast.type, 'stmts');
let node;
for (var i = 0; i < ast.stmts.length; i++) {
node = ast.stmts[i];
this.visitStmt(node, level, env);
}
if (node) {
//find the last node's back comment
let comments = DSL.comment.getBackComments(this.comments, node.tokenRange[1]);
this.visitComments(comments, level);
}
if (ast.stmts.length === 0) {
//empty block's comment
let comments = DSL.comment.getBetweenComments(this.comments, ast.tokenRange[0], ast.tokenRange[1]);
this.visitComments(comments, level);
}
}
visitYieldReturnBody(ast, funcName, level, env) {
let args = [];
args.push({
name: '_yield chan',
type: env.returnType.valueType,
});
if(env.hasThrow) {
args.push({
name: '_yieldErr chan error',
});
}
let vars = this.getStmtsVars(ast.stmts.stmts);
this.emit(`${funcName}(_yield`, level);
if(env.hasThrow) {
this.emit(', _yieldErr, ');
}
const argsMap = {};
let params = new Set();
for (let i = 0; i < vars.length; i++) {
const arg = vars[i];
params.add(arg.name);
argsMap[arg.name] = arg.type;
}
params = [...params];
this.emit(params.join(', '));
params.map((name) => {
const variable = (env.pointerParams && env.pointerParams.includes(name)) ? false : true;
args.push({
name,
type: argsMap[name],
variable,
});
});
this.emit(')\n');
if(env.hasThrow) {
this.emit('_err = <-_yieldErr\n', level);
}
this.visitAPIErrCatch(level, env);
this.emit('return\n', level);
this.yieldFunc.push({
args,
functionBody: ast,
name: funcName,
pointerParams: env.pointerParams,
});
}
visitReturnBody(ast, apiName, level, env) {
assert.equal(ast.type, 'returnBody');
const funcName = `${_lowerFirst(apiName)}_opResponse`;
if(_isIterator(env.returnType)) {
this.visitYieldReturnBody(ast, funcName, level, env);
return;
}
const errCounts = this.stmtsErrCount(ast.stmts.stmts);
if(errCounts > 1) {
const args = this.getStmtsVars(ast.stmts.stmts);
this.tryFunc.push({
args,
functionBody: ast.stmts,
returnType: env.returnType,
name: funcName,
pointerParams: env.pointerParams,
});
if (_name(env.returnType) && _name(env.returnType) !== 'void') {
this.emit(`_result, _err = ${funcName}`, level);
} else {
this.emit(`_err = ${funcName}`, level);
}
this.visitTryArgs(args, level, env);
this.emit('\n');
this.visitAPIErrCatch(level, env);
let err = '_err';
if(!env.runtimeBody) {
err = 'nil';
}
if(_name(env.returnType) === 'void' ) {
this.emit(`return ${err}\n`, level);
} else {
this.emit(`return _result, ${err}\n`, level);
}
return;
}
this.visitStmts(ast.stmts, level, env);
}
visitYieldFunction(ast, level, env) {
env.routine = true;
const args = [];
const pointerParams = [];
this.emit('defer close(_yield)\n', level);
args.push({
name: '_yield chan',
type: ast.returnType.valueType,
});
if(env.hasThrow) {
this.emit('defer close(_yieldErr)\n', level);
args.push({
name: '_yieldErr chan error',
});
}
const functionName = `${_lowerFirst(env.funcName)}_opYieldFunc`;
this.emit(`${functionName}(_yield`, level);
if(env.hasThrow) {
this.emit(', _yieldErr');
}
for (var i = 0; i < ast.params.params.length; i++) {
this.emit(', ');
const node = ast.params.params[i];
assert.equal(node.type, 'param');
const name = _avoidReserveName(_name(node.paramName));
pointerParams.push(name);
this.emit(name);
if (node.paramType) {
args.push({
name,
type: node.paramType,
});
}
}
this.emit(')\n');
this.yieldFunc.push({
name: functionName,
args,
functionBody: ast.functionBody,
pointerParams,
});
this.emit('return\n', level);
}
visitFunctionBody(ast, level, env) {
assert.equal(ast.type, 'functionBody');
this.visitStmts(ast.stmts, level, env);
const stmts = ast.stmts.stmts;
const length = ast.stmts.stmts.length;
if (_name(env.returnType) === 'void' && env.hasThrow && (length === 0 || (stmts[length - 1].type !== 'return' &&
stmts[length - 1].type !== 'throw'))) {
if(env.yieldErrDeal) {
this.emit(`_err = <- _yieldErr\n`, level);
}
this.emit(`return _err\n`, level);
}
if (length === 0 || this.functionBodyRetrun(stmts[length - 1], env)) {
if (_name(env.returnType) !== 'void' && env.hasThrow) {
if(env.yieldErrDeal) {
this.emit(`_err = <- _yieldErr\n`, level);
}
this.emit(`return _result, _err\n`, level);
} else if (_name(env.returnType) !== 'void' && !env.hasThrow) {
this.emit(`return _result\n`, level);
}
}
}
functionBodyRetrun(stmt, env) {
if(stmt.type === 'return') {
return false;
}
if(stmt.type === 'throw') {
return false;
}
if(!(stmt.type !== 'if' || !stmt.elseStmts)) {
return false;
}
if(stmt.type === 'try' && (stmt.tryBlock && stmt.tryBlock.stmts.length > 0
&& !this.functionBodyRetrun(stmt.tryBlock.stmts[stmt.tryBlock.stmts.length - 1])) && !env.hasReturn) {
return false;
}
return true;
}
eachFunc(ast, level, predefined, apis) {
const env = {
predefined,
apis,
local: new Map(),
returnType: ast.returnType,
hasThrow: ast.isAsync || ast.hasThrow,
yieldFunc: _isIterator(ast.returnType),
nestFuncParamName: new Map(),
nestFuncParamNameSubscript: { 'count': 0 }
};
const functionName = _name(ast.functionName);
this.visitAnnotation(ast.annotation, level);
let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]);
this.visitComments(comments, level);
const name = _format(functionName);
env.funcName = name;
if (this.exec && name === 'Main') {
this.emit(`func _main `, level);
} else if(name.startsWith('$') && this.builtin[name]){
const method = name.replace('$', '');
this.builtin[name][method](ast.args, level);
return;
} else if (ast.isStatic) {
this.emit(`func ${name} `, level);
} else {
this.emit(`func (client *${this.structName}) ${name} `, level);
}
this.visitParams(ast.params, level, env);
this.visitReturnType(ast, level, env);
if (ast.functionBody) {
if(_isIterator(ast.returnType)) {
this.visitYieldFunction(ast, level + 1, env);
} else {
this.visitFunctionBody(ast.functionBody, level + 1, env);
}
} else {
this.emit(`panic("No Support!")\n`, level + 1);
}
this.emit(`}\n`, level);
this.emit(`\n`, level);
}
visitAPIErrCatch(level, env) {
this.emit('if _err != nil {\n', level);
if(env.runtimeBody) {
this.emit('retriesAttempted++\n', level + 1);
this.emit('retryPolicyContext = &dara.RetryPolicyContext{\n', level + 1);
this.emit('RetriesAttempted: retriesAttempted,\n', level + 2);
this.emit('HttpRequest: request_,\n', level + 2);
this.emit('HttpResponse: response_,\n', level + 2);
this.emit('Exception: _err,\n', level + 2);
this.emit('}\n', level + 1);
this.emit('_resultErr = _err\n', level + 1);
this.emit('continue\n', level + 1);
} else {
this.emit('return ', level + 1);
if(_name(env.returnType) !== 'void') {
this.emit('nil, ');
}
this.emit('_err\n');
}
this.emit('}\n', level);
this.emit('\n');
}
eachAPI(ast, level, predefined) {
// if (ast.annotation) {
// this.emit(`${_anno(ast.annotation.value)}\n`, level);
// }
const env = {
// params, paramMap, returnType,
predefined,
returnType: ast.returnType,
runtimeBody: ast.runtimeBody,
local: new Map(),
hasThrow: true,
yieldFunc: _isIterator(ast.returnType),
nestFuncParamName: new Map(),
nestFuncParamNameSubscript: { 'count': 0 },
};
const apiName = _name(ast.apiName);
this.visitAnnotation(ast.annotation, level);
let comments = DSL.comment.getFrontComments(this.comments, ast.tokenRange[0]);
this.visitComments(comments, level);
// func (b *Buffer) Next(n int) []byte {
this.emit(`func (client *${this.structName}) ${_format(apiName)}`, level);
this.visitParams(ast.params, level, env);
this.visitReturnType(ast, level, env);
// this.emit(` (*map[string]interface{}, error) {\n`);
let baseLevel = ast.runtimeBody ? level + 1 : level;
if(env.yieldFunc) {
this.emit('defer close(_yield)\n', level + 1);
if(env.hasThrow) {
this.emit('defer close(_yieldErr)\n', level + 1);
}
}
// api level
if (ast.runtimeBody) {
this.visitRuntimeBefore(ast.runtimeBody, level + 1, env);
}
// temp level
this.visitAPIBody(ast.apiBody, baseLevel + 1, env);
// if (ast.runtimeBody) {
// this.emit(`_lastRequest = ${REQUEST}\n`, baseLevel + 1);
// }
this.emit(`${RESPONSE}, _err := dara.DoRequest(${REQUEST}`, baseLevel + 1);
if (ast.runtimeBody) {
this.emit(`, _runtime`);
} else {
this.emit(`, nil`);
}
this.emit(`)\n`);
this.visitAPIErrCatch(baseLevel + 1, env);
if (ast.returns) {
this.visitReturnBody(ast.returns, apiName, baseLevel + 1, env);
} else {
this.visitDefaultReturnBody(baseLevel + 1, env);
}
if (ast.runtimeBody) {
this.emit('}\n', level + 1);
if(env.yieldFunc) {
this.emit(`_yieldErr <- _resultErr\n`, level + 1);
this.emit(`return\n`, level + 1);
} else {
if(!this.noCompatible) {
this.emit(`if dara.BoolValue(client.DisableSDKError) != true {\n`, level + 1);
this.emit(`_resultErr = dara.TeaSDKError(_resultErr)\n`, level + 2);
this.emit(`}\n`, level + 1);
}
this.emit(`return _result, _resultErr\n`, level + 1);
}
}
this.emit(`}\n\n`, level);
}
visitDefaultReturnBody(level, env) {
this.emit('\n');
if (_name(env.returnType) === 'void') {
this.emit(`return nil\n`, level);
} else {
this.emit('return nil, nil\n', level);
}
// this.emit(`"statusCode": ${RESPONSE}.statusCode,\n`, level + 1);
// this.emit(`"statusMessage": ${RESPONSE}.statusMessage,\n`, level + 1);
// this.emit(`"headers": ${RESPONSE}.headers,\n`, level + 1);
// this.emit(`"body": client.Auto__(${RESPONSE}),\n`, level + 1);
}
moduleBefore(ast, main, level) {
this.mainModule = main;
let beginToken = 0;
if(ast.imports.length > 0) {
const lastIndex = ast.imports.length - 1;
beginToken = ast.imports[lastIndex].tokenRange[0];
}
const beginNotes = DSL.note.getNotes(this.notes, beginToken, ast.moduleBody.nodes[0].tokenRange[0]);
const part = beginNotes.find(note => note.note.lexeme === '@go');
if(part && part.arg.value) {
this.emit(_string(part.arg.value));
}
}
visitHeader(__module, packageName, level) {
this.header = '';
if (!this.editable) {
this.header += `// This file is auto-generated, don't edit it. Thanks.\n`;
}
this.header += `package ${packageName}\n\n`;
this.header += `import (\n`;
const imports = [];
if(this.mainModule) {
if (this.exec) {
imports.push(` "os"`);
}
if (this.goPackages.length > 0) {
this.goPackages.map(pack => {
imports.push(pack);
});
}
}
this.imports.map(im => {
let tmpStr = ' ';
if(im.aliasId) {
tmpStr += `${_importFilter(_format(im.aliasId).toLowerCase())} `;
}
tmpStr += `"${im.pkgName}"`;
imports.push(tmpStr);
});
imports.push(` "github.com/alibabacloud-go/tea/dara"`);
this.builtinModule.forEach(builtinModule => {
let content = ' ';
if(builtinModule.name) {
content += `${builtinModule.name} `;
}
content += `"${builtinModule.path}"`;
imports.push(content);
});
this.header += [...new Set(imports)].join('\n') + '\n)\n\n';
}
modelBefore(filepath) {
// Nothing
const targetPath = path.join(this.outputDir, filepath);
_deleteWithSuffix(path.dirname(targetPath), '_model.go');
_deleteWithSuffix(path.dirname(targetPath), '_error.go');
}
modelAfter() {
// Nothing
}
apiBefore(__module, level) {
this.emit(`\n`);
}
init(level) {
// Nothing
}
apiAfter(__module, level = 0) {
// Nothing
}
wrapBefore(__module, level) {
this.emit(`\n`);
}
wrapAfter(filepath, level) {
// Nothing
}
moduleAfter() {
this.uselessPack.forEach((pack) => {
this.header = this.header.replace(` "${pack}"\n`, '');
});
}
visitTryArgs(args) {
const params = new Set();
this.emit('(');
for (let i = 0; i < args.length; i++) {
const arg = args[i];
params.add(arg.name);
}
this.emit([...params].join(', '));
this.emit(')');
}
visitTryParams(args, env) {
const argsMap = {};
let params = new Set();
this.emit('(');
for (let i = 0; i < args.length; i++) {
const arg = args[i];
params.add(arg.name);
argsMap[arg.name] = arg.type;
}
params = [...params];
params.map((param, index) => {
if(param === '_yield') {
env.yieldFunc = true;
}
this.emit(`${param} `);
if(env.pointerParams && env.pointerParams.includes(param)){
this.visitPointerType(argsMap[param]);
} else {
this.visitType(argsMap[param]);
}
if(index !== params.length - 1) {
this.emit(', ');
}
});
this.emit(')');
}
eachYieldFunc(ast, level) {
const env = {
local: new Map(),
returnType: {
name: 'void'
},
hasThrow: false,
yieldFunc: true,
pointerParams: ast.pointerParams,
};
this.emit(`func ${ast.name}(`, level);
for(let i = 0; i < ast.args.length; i++) {
const arg = ast.args[i];
if (arg.name === '_yieldErr chan error') {
env.hasThrow = true;
}
this.emit(arg.name);
if(arg.type) {
this.emit(` `);
if(!arg.variable) {
this.visitPointerType(arg.type, level, env);
} else {
this.visitType(arg.type, level, env);
}
}
if(i !== ast.args.length - 1){
this.emit(', ');
}
}
this.emit(') {\n');
this.visitStmts(ast.functionBody.stmts, level + 1, env);
this.emit(`}\n`, level);
this.emit(`\n`, level);
}
eachTryFunc(ast, level) {
const env = {
local: new Map(),
returnType: ast.returnType,
hasThrow: true,
pointerParams: ast.pointerParams || [],
};
this.emit(`func ${ast.name} `, level);
this.visitTryParams(ast.args, env);
this.emit('(');
if (_name(env.returnType) !== 'void') {
this.emit(` _result `);
this.visitPointerType(ast.returnType, level, env);
this.emit(', ');
}
this.emit('_err error) {\n');
this.visitStmts(ast.functionBody, level + 1, env);
const stmts = ast.functionBody.stmts;
const length = ast.functionBody.stmts.length;
if (_name(env.returnType) === 'void') {
this.emit(`return _err\n`, level + 1);
} else if ((length === 0 || (stmts[length - 1].type !== 'return' && stmts[length - 1].type !== 'throw' &&
(stmts[length - 1].type !== 'if' || !stmts[length - 1].elseStmts)))) {
this.emit(`return _result, _err\n`, level + 1);
}
this.emit(`}\n`, level);
this.emit(`\n`, level);
}
isNotBuiltin(ast) {
if(ast.type === 'call') {
if (ast.left.type === 'method_call') {
const name = _format(_name(ast.left.id));
if (name.startsWith('$') && this.builtin[name]) {
return false;
}
} else if (ast.left.type === 'instance_call') {
if(ast.builtinModule && this.builtin[ast.builtinModule]) {
return false;
}
} else if (ast.left.type === 'static_call') {
if(ast.left.id.type === 'builtin_module') {
return false;
}
}
}
return true;
}
typeRelover(type, module) {
if (module && module.idType === 'module') {
const aliasId = _name(module);
if (this.importsTypedef[aliasId] && this.importsTypedef[aliasId][type.lexeme]) {
let index = this.uselessPack.indexOf(this.importsTypedef[aliasId][type.lexeme].import);
if (index > -1) {
this.uselessPack.splice(index, 1);
}
this.imports.push({
pkgName: this.importsTypedef[aliasId][type.lexeme].import,
});
return this.importsTypedef[aliasId][type.lexeme].type;
}
}
if (type.idType === 'typedef' && this.typedef[type.lexeme]) {
if (this.typedef[type.lexeme].import && this.typedef[type.lexeme].type) {
let index = this.uselessPack.indexOf(this.typedef[type.lexeme].import);
if (index > -1) {
this.uselessPack.splice(index, 1);
}
this.imports.push({
pkgName: this.typedef[type.lexeme].import,
});
return this.typedef[type.lexeme].type;
}
}
if (this.predefined[_name(type)] && this.predefined[_name(type)].isException) {
return `${_name(type)}Error`;
}
return _name(type);
}
getType(name, pointer = true) {
const type = pointer ? _pointerType(name) : _type(name);
if(type.includes('io.')) {
this.imports.push({
pkgName: 'io',
});
}
return type;
}
}
module.exports = Visitor;