visitExpr()

in lib/semantic.js [2080:2203]


  visitExpr(ast, env) {
    if (ast.type === 'string') {
      // noop();
    } else if (ast.type === 'number') {
      // noop();
    } else if (ast.type === 'boolean') {
      // noop();
    } else if (ast.type === 'null') {
      // noop();
    } else if (ast.type === 'group') {
      this.visitExpr(ast.expr, env);
    } else if (ast.type === 'property_access') {
      this.checkProperty(ast, env);
    } else if (ast.type === 'object') {
      this.visitObject(ast, env);
    } else if (ast.type === 'variable') {
      this.checkId(ast.id, env);
    } else if (ast.type === 'virtualVariable') {
      this.checkVid(ast.vid, env);
    } else if (ast.type === 'template_string') {
      for (var i = 0; i < ast.elements.length; i++) {
        var item = ast.elements[i];
        if (item.type === 'expr') {
          this.visitExpr(item.expr, env);
        }
      }
    } else if (ast.type === 'call') {
      this.visitCall(ast, env);
    } else if (ast.type === 'construct') {
      this.visitConstruct(ast, env);
    } else if (ast.type === 'construct_model') {
      this.visitConstructModel(ast, env);
    } else if (ast.type === 'array') {
      this.visitArray(ast, env);
    } else if (ast.type === 'and' || ast.type === 'or') {
      this.visitExpr(ast.left, env);
      // the expr type should be boolean
      if (!this.isBooleanType(ast.left, env)) {
        this.error(`the left expr must be boolean type`, ast.left);
      }
      this.visitExpr(ast.right, env);
      // the expr type should be boolean
      if (!this.isBooleanType(ast.right, env)) {
        this.error(`the right expr must be boolean type`, ast.right);
      }
    } else if (ast.type === 'not') {
      this.visitExpr(ast.expr, env);
      if (!this.isBooleanType(ast.expr, env)) {
        this.error(`the expr after ! must be boolean type`, ast.expr);
      }
    } else if (ast.type === 'increment' || ast.type === 'decrement') {
      this.visitExpr(ast.expr, env);
      if(!this.isNumberType(ast.expr, env)) {
        this.error(`the increment/decrement expr must be number type`, ast.expr);
      }
    } else if (isCompare(ast.type)) {
      this.visitExpr(ast.left, env);
      const leftType = this.getExprType(ast.left, env);
      this.visitExpr(ast.right, env);
      const rightType = this.getExprType(ast.right, env);
      if(ast.type !== 'eq' && ast.type !== 'neq' &&
      (!(leftType.type === 'basic' && isNumber(leftType.name)) || 
      !(rightType.type === 'basic' && isNumber(rightType.name)))) {
        this.error(`${ast.type} can only operate number type, ` +
        `but left: ${display(leftType)} and right: ${display(rightType)}`, ast);
      }
      if (!eql([leftType], [rightType])) {
        this.error(`${display(leftType)} can only compare with ${display(leftType)} type, ` +
          `but ${display(rightType)}`, ast);
      }
    } else if (ast.type === 'add') {
      this.visitExpr(ast.left, env);
      const leftType = this.getExprType(ast.left, env);
      this.visitExpr(ast.right, env);
      const rightType = this.getExprType(ast.right, env);
      if (!eql([leftType], [rightType])) {
        this.error(`${display(leftType)} can only add with ${display(leftType)} type, ` +
          `but ${display(rightType)}`, ast);
      }
    } else if (ast.type === 'subtract' || ast.type === 'div' || ast.type === 'multi') {
      this.visitExpr(ast.left, env);
      const leftType = this.getExprType(ast.left, env);
      this.visitExpr(ast.right, env);
      const rightType = this.getExprType(ast.right, env);
      if(!(leftType.type === 'basic' && isNumber(leftType.name)) || 
      !(rightType.type === 'basic' && isNumber(rightType.name))) {
        this.error(`${ast.type} can only operate number type, ` +
        `but left: ${display(leftType)} and right: ${display(rightType)}`, ast);
      }
      if (!eql([leftType], [rightType])) {
        this.error(`${display(leftType)} can only ${ast.type} with ${display(leftType)} type, ` +
          `but ${display(rightType)}`, ast);
      }
    } else if (ast.type === 'super') {
      this.visitSuperCall(ast, env);
    } else if (ast.type === 'map_access') {
      let mainType;
      if (ast.propertyPath) {
        this.checkProperty(ast, env);
        mainType = this.calculatePropertyType(ast, env);
      } else {
        this.checkId(ast.id, env);
        mainType = this.getVariableType(ast.id, env);
      }

      this.visitExpr(ast.accessKey, env);

      if (mainType.type === 'map') {
        if (!this.isStringType(ast.accessKey, env)) {
          this.error(`The key expr type must be string type`, ast.accessKey);
        }
      } else if (mainType.type === 'array') {
        ast.type = 'array_access';
        if (!this.isNumberType(ast.accessKey, env)) {
          this.error(`The key expr type must be number type`, ast.accessKey);
        }
      } else {
        this.error(`the [] form only support map or array type`, ast.accessKey);
      }
    } else {
      throw new Error('unimplemented.');
    }
    ast.inferred = this.getExprType(ast, env);
  }