visitRuntimeBefore()

in lib/generator.js [1113:1715]


  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);
    }
  }