blob: cc563ba458353f6f9378fa0ab2cf0b765fb2a305 [file]
/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Expr
import Lean.Environment
import Lean.Attributes
import Lean.ProjFns
import Lean.Meta.Basic
namespace Lean
inductive ExternEntry where
| adhoc (backend : Name)
| inline (backend : Name) (pattern : String)
| standard (backend : Name) (fn : String)
| foreign (backend : Name) (fn : String)
/--
- `@[extern]`
encoding: ```.entries = [adhoc `all]```
- `@[extern "level_hash"]`
encoding: ```.entries = [standard `all "levelHash"]```
- `@[extern cpp "lean::string_size" llvm "lean_str_size"]`
encoding: ```.entries = [standard `cpp "lean::string_size", standard `llvm "leanStrSize"]```
- `@[extern cpp inline "#1 + #2"]`
encoding: ```.entries = [inline `cpp "#1 + #2"]```
- `@[extern cpp "foo" llvm adhoc]`
encoding: ```.entries = [standard `cpp "foo", adhoc `llvm]```
- `@[extern 2 cpp "io_prim_println"]`
encoding: ```.arity? = 2, .entries = [standard `cpp "ioPrimPrintln"]```
-/
structure ExternAttrData where
arity? : Option Nat := none
entries : List ExternEntry
deriving Inhabited
-- def externEntry := leading_parser optional ident >> optional (nonReservedSymbol "inline ") >> strLit
-- @[builtin_attr_parser] def extern := leading_parser nonReservedSymbol "extern " >> optional numLit >> many externEntry
private def syntaxToExternAttrData (stx : Syntax) : AttrM ExternAttrData := do
let arity? := if stx[1].isNone then none else some <| stx[1][0].isNatLit?.getD 0
let entriesStx := stx[2].getArgs
if entriesStx.size == 0 && arity? == none then
return { entries := [ ExternEntry.adhoc `all ] }
let mut entries := #[]
for entryStx in entriesStx do
let backend := if entryStx[0].isNone then `all else entryStx[0][0].getId
let str ← match entryStx[2].isStrLit? with
| none => throwErrorAt entryStx[2] "string literal expected"
| some str => pure str
if entryStx[1].isNone then
entries := entries.push <| ExternEntry.standard backend str
else
entries := entries.push <| ExternEntry.inline backend str
return { arity? := arity?, entries := entries.toList }
@[extern "lean_add_extern"]
opaque addExtern (env : Environment) (n : Name) : ExceptT String Id Environment
builtin_initialize externAttr : ParametricAttribute ExternAttrData ←
registerParametricAttribute {
name := `extern
descr := "builtin and foreign functions"
getParam := fun _ stx => syntaxToExternAttrData stx
afterSet := fun declName _ => do
let mut env ← getEnv
if env.isProjectionFn declName || env.isConstructor declName then do
env ← ofExcept <| addExtern env declName
setEnv env
else
pure ()
}
@[export lean_get_extern_attr_data]
def getExternAttrData? (env : Environment) (n : Name) : Option ExternAttrData :=
externAttr.getParam? env n
private def parseOptNum : Nat → String.Iterator → Nat → String.Iterator × Nat
| 0, it, r => (it, r)
| n+1, it, r =>
if !it.hasNext then (it, r)
else
let c := it.curr
if '0' <= c && c <= '9'
then parseOptNum n it.next (r*10 + (c.toNat - '0'.toNat))
else (it, r)
def expandExternPatternAux (args : List String) : Nat → String.Iterator → String → String
| 0, _, r => r
| i+1, it, r =>
if ¬ it.hasNext then r
else let c := it.curr
if c ≠ '#' then expandExternPatternAux args i it.next (r.push c)
else
let it := it.next
let (it, j) := parseOptNum it.remainingBytes it 0
let j := j-1
expandExternPatternAux args i it (r ++ args.getD j "")
def expandExternPattern (pattern : String) (args : List String) : String :=
expandExternPatternAux args pattern.length pattern.mkIterator ""
def mkSimpleFnCall (fn : String) (args : List String) : String :=
fn ++ "(" ++ ((args.intersperse ", ").foldl (·++·) "") ++ ")"
def ExternEntry.backend : ExternEntry → Name
| ExternEntry.adhoc n => n
| ExternEntry.inline n _ => n
| ExternEntry.standard n _ => n
| ExternEntry.foreign n _ => n
def getExternEntryForAux (backend : Name) : List ExternEntry → Option ExternEntry
| [] => none
| e::es =>
if e.backend == `all then some e
else if e.backend == backend then some e
else getExternEntryForAux backend es
def getExternEntryFor (d : ExternAttrData) (backend : Name) : Option ExternEntry :=
getExternEntryForAux backend d.entries
def isExtern (env : Environment) (fn : Name) : Bool :=
getExternAttrData? env fn |>.isSome
/-- We say a Lean function marked as `[extern "<c_fn_nane>"]` is for all backends, and it is implemented using `extern "C"`.
Thus, there is no name mangling. -/
def isExternC (env : Environment) (fn : Name) : Bool :=
match getExternAttrData? env fn with
| some { entries := [ ExternEntry.standard `all _ ], .. } => true
| _ => false
def getExternNameFor (env : Environment) (backend : Name) (fn : Name) : Option String := do
let data? ← getExternAttrData? env fn
let entry ← getExternEntryFor data? backend
match entry with
| ExternEntry.standard _ n => pure n
| ExternEntry.foreign _ n => pure n
| _ => failure
private def getExternConstArity (declName : Name) : CoreM Nat := do
let fromSignature : Unit → CoreM Nat := fun _ => do
let cinfo ← getConstInfo declName
let (arity, _) ← (Meta.forallTelescopeReducing cinfo.type fun xs _ => pure xs.size : MetaM Nat).run
return arity
let env ← getEnv
match getExternAttrData? env declName with
| none => fromSignature ()
| some data => match data.arity? with
| some arity => return arity
| none => fromSignature ()
@[export lean_get_extern_const_arity]
def getExternConstArityExport (env : Environment) (declName : Name) : IO (Option Nat) := do
try
let (arity, _) ← (getExternConstArity declName).toIO { fileName := "<compiler>", fileMap := default } { env := env }
return some arity
catch
| IO.Error.userError _ => return none
| _ => return none
end Lean