getExprType()

in lib/semantic.js [2680:2877]


  getExprType(ast, env) {
    if (!ast) {
      return _basic('void');
    }

    if (ast.inferred) {
      return ast.inferred;
    }

    if (ast.type === 'property_access' || ast.type === 'property') {
      return this.calculatePropertyType(ast, env);
    }

    if (ast.type === 'map_access') {
      let type;
      if (ast.propertyPath) {
        type = this.calculatePropertyType(ast, env);
      } else {
        type = this.getVariableType(ast.id, env);
      }

      if (type.type === 'array') {
        return type.itemType;
      } else if (type.type === 'map') {
        return type.valueType;
      }
    }

    if (ast.type === 'array_access') {
      let type;
      if (ast.propertyPath) {
        type = this.calculatePropertyType(ast, env);
      } else {
        type = this.getVariableType(ast.id, env);
      }
      
      if (type.type === 'array') {
        return type.itemType;
      }
    }

    if (ast.type === 'string') {
      return _basic('string');
    }

    if (ast.type === 'number') {
      return _basic(ast.value.type);
    }

    if (ast.type === 'boolean') {
      return _basic('boolean');
    }

    if (ast.type === 'object') {
      if (ast.fields.length === 0) {
        return {
          type: 'map',
          keyType: _basic('string'),
          valueType: _basic('any')
        };
      }

      var current;
      var same = true;
      for (let i = 0; i < ast.fields.length; i++) {
        const field = ast.fields[i];
        if (field.type === 'objectField') {
          let type = this.getExprType(field.expr, env);
          if (current && !isSameType(current, type)) {
            same = false;
            break;
          }
          current = type;
        } else if (field.type === 'expandField') {
          let type = this.getExprType(field.expr, env);
          if (type.type === 'map') {
            if (current && !isSameType(current, type.valueType)) {
              same = false;
              break;
            }
            current = type.valueType;
          } else {
            same = false;
            break;
          }
        }
      }

      return {
        type: 'map',
        keyType: _basic('string'),
        valueType: same ? current : _basic('any')
      };
    }

    if (ast.type === 'variable') {
      return this.getVariableType(ast.id, env);
    }

    if (ast.type === 'virtualVariable') {
      const type = this.getInstanceProperty(ast.vid.lexeme);
      return this.getType(type.value);
    }

    if (ast.type === 'null') {
      return _basic('null');
    }

    if (ast.type === 'template_string') {
      return _basic('string');
    }

    if (ast.type === 'call') {
      assert.ok(ast.inferred);
      return ast.inferred;
    }

    if (ast.type === 'super') {
      return {
        type: 'module_instance',
        name: this.parentModuleId,
        parentModuleIds: this.getParentModuleIds(this.parentModuleId),
      };
    }

    if (ast.type === 'construct') {
      return {
        type: 'module_instance',
        name: ast.aliasId.lexeme,
        parentModuleIds: this.getParentModuleIds(ast.aliasId.lexeme),
      };
    }

    if (ast.type === 'construct_model') {
      if (ast.aliasId.isModel) {
        const model = this.getModel([ast.aliasId.lexeme, ...ast.propertyPath.map((item) => {
          return item.lexeme;
        })].join('.'));
        model.isException = ast.aliasId.isException;
        return model;
      }

      const moduleName = ast.aliasId.lexeme;
      const checker = this.dependencies.get(moduleName);
      const model = checker.getModel(ast.propertyPath.map((item) => {
        return item.lexeme;
      }).join('.'), ast.aliasId.lexeme);
      model.isException = ast.aliasId.isException;
      return model;
    }

    if (ast.type === 'array') {
      if (ast.items.length === 0) {
        return {
          type: 'array',
          itemType: _basic('any')
        };
      }

      let current;
      let same = true;
      for (let i = 0; i < ast.items.length; i++) {
        const type = this.getExprType(ast.items[i], env);
        if (current && !isSameType(current, type)) {
          same = false;
          break;
        }
        current = type;
      }
      return {
        type: 'array',
        itemType: same ? current : _basic('any')
      };
    }
    if (ast.type === 'group') {
      return this.getExprType(ast.expr, env);
    }

    if (isCompare(ast.type)) {
      return _basic('boolean');
    }

    if (ast.type === 'and' || ast.type === 'or') {
      return _basic('boolean');
    }

    if (ast.type === 'increment' || ast.type === 'decrement') {
      return this.getExprType(ast.expr, env);
    }

    if (ast.type === 'add' || ast.type === 'subtract'
    || ast.type === 'div' || ast.type === 'multi') {
      return this.getExprType(ast.right, env);
    }

    console.log(ast);
    throw new Error('can not get type');
  }