// 基于语法树的语义检查与名称绑定。
#pragma once

#include <string>
#include <unordered_map>
#include <vector>

#include "SysYParser.h"

enum class SemanticType {
  Void,
  Int,
  Float,
};

struct ScalarConstant {
  SemanticType type = SemanticType::Int;
  double number = 0.0;
};

struct ObjectBinding {
  enum class DeclKind {
    Var,
    Const,
    Param,
  };

  std::string name;
  SemanticType type = SemanticType::Int;
  DeclKind decl_kind = DeclKind::Var;
  bool is_array_param = false;
  std::vector<int> dimensions;
  const SysYParser::VarDefContext* var_def = nullptr;
  const SysYParser::ConstDefContext* const_def = nullptr;
  const SysYParser::FuncFParamContext* func_param = nullptr;
  bool has_const_value = false;
  ScalarConstant const_value;
};

struct FunctionBinding {
  std::string name;
  SemanticType return_type = SemanticType::Int;
  std::vector<ObjectBinding> params;
  const SysYParser::FuncDefContext* func_def = nullptr;
  bool is_builtin = false;
};

class SemanticContext {
 public:
  void BindObjectUse(const SysYParser::LValContext* use, ObjectBinding binding);
  const ObjectBinding* ResolveObjectUse(
      const SysYParser::LValContext* use) const;

  void BindFunctionCall(const SysYParser::UnaryExpContext* call,
                        FunctionBinding binding);
  const FunctionBinding* ResolveFunctionCall(
      const SysYParser::UnaryExpContext* call) const;

  void RegisterFunction(FunctionBinding binding);
  const FunctionBinding* ResolveFunction(const std::string& name) const;

 private:
  std::unordered_map<const SysYParser::LValContext*, ObjectBinding> object_uses_;
  std::unordered_map<const SysYParser::UnaryExpContext*, FunctionBinding>
      function_calls_;
  std::unordered_map<std::string, FunctionBinding> functions_;
};

SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit);
