/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import init.lean.expr
import init.lean.declaration

namespace Lean
namespace Compiler

def neutralExpr : Expr       := Expr.const `_neutral []
def unreachableExpr : Expr   := Expr.const `_unreachable []
def objectType : Expr        := Expr.const `_obj []
def voidType : Expr          := Expr.const `_void []
def mkLcProof (pred : Expr)  := Expr.app (Expr.const `lcProof []) pred

namespace atMostOnce

structure AtMostOnceData :=
(found result : Bool)

def Visitor := AtMostOnceData → AtMostOnceData

@[inline] def seq (f g : Visitor) : Visitor :=
λ d, match f d with
| ⟨found, false⟩ := ⟨found, false⟩
| other          := g other

instance : HasAndthen Visitor :=
⟨seq⟩

@[inline] def skip : Visitor := id

@[inline] def visitFVar (x y : Name) : Visitor
| d@{result := false, ..} := d
| {found := false, result := true} := {found := x == y, result := true}
| {found := true,  result := true} := {found := true, result := x != y}

def visit (x : Name) : Expr → Visitor
| (Expr.fvar y)       := visitFVar y x
| (Expr.app f a)      := visit a >> visit f
| (Expr.lam _ _ d b)  := visit d >> visit b
| (Expr.pi _ _ d b)   := visit d >> visit b
| (Expr.elet _ t v b) := visit t >> visit v >> visit b
| (Expr.mdata _ e)    := visit e
| (Expr.proj _ _ e)   := visit e
| _                   := skip

end atMostOnce

/-- Return true iff the free variable with id `x` occurs at most once in `e` -/
@[export lean.at_most_once_core]
def atMostOnce (e : Expr) (x : Name) : Bool :=
let {result := result, ..} := atMostOnce.visit x e {found := false, result := true} in
result

/- Helper functions for creating auxiliary names used in compiler passes. -/

@[export lean.mk_eager_lambda_lifting_name_core]
def mkEagerLambdaLiftingName (n : Name) (idx : Nat) : Name :=
Name.mkString n ("_elambda_" ++ toString idx)

@[export lean.is_eager_lambda_lifting_name_core]
def isEagerLambdaLiftingName : Name → Bool
| (Name.mkString p s)  := "_elambda".isPrefixOf s || isEagerLambdaLiftingName p
| (Name.mkNumeral p _) := isEagerLambdaLiftingName p
| _ := false

/-- Return the name of new definitions in the a given declaration.
    Here we consider only declarations we generate code for.
    We use this definition to implement `add_and_compile`. -/
@[export lean.get_decl_names_for_code_gen_core]
private def getDeclNamesForCodeGen : Declaration → List Name
| (Declaration.defnDecl { name := n, .. })   := [n]
| (Declaration.mutualDefnDecl defs)          := defs.map $ λ d, d.name
| _                                          := []

end Compiler
end Lean
