type typ =
  | TInt
  | TBool
  | TVoid
  | TStruct of string
  | TArray of typ

type binop =
  | Add
  | Sub
  | Mul
  | Div
  | Mod
  | And
  | Or
  | Eq
  | Ne
  | Lt
  | Le
  | Gt
  | Ge

type unop = Neg | Not

type expr =
  | IntLit of int
  | BoolLit of bool
  | Var of string
  | Binop of binop * expr * expr
  | Unop of unop * expr
  | Assign of expr * expr
  | Call of expr * expr list
  | ArrayGet of expr * expr
  | StructGet of expr * string

type stmt =
  | VarDecl of typ * string * expr option
  | Expr of expr
  | If of expr * stmt list * stmt list
  | ForEach of typ * string * expr * stmt list
  | Return of expr option
  | Block of stmt list

type func = {
  name : string;
  params : (typ * string) list;
  ret : typ;
  body : stmt list;
}

type struct_def = {
  sname : string;
  fields : (typ * string) list;
}

type top =
  | TopStruct of struct_def
  | TopFunc of func
  | TopGlobalVar of typ * string * expr option

type program = top list

val string_of_typ : typ -> string
val string_of_program : program -> string
val parse_string : string -> (program, string) result
val type_check : program -> (unit, string) result
val parse_and_type_check : string -> (program, string) result
