215 lines
7.3 KiB
Text
215 lines
7.3 KiB
Text
/-
|
||
Copyright (c) 2024 Joseph Tooby-Smith. All rights reserved.
|
||
Released under Apache 2.0 license.
|
||
Authors: Joseph Tooby-Smith
|
||
-/
|
||
import Mathlib.Lean.Expr.Basic
|
||
import ImportGraph.RequiredModules
|
||
/-!
|
||
|
||
## Basic Lean meta programming commands
|
||
|
||
-/
|
||
|
||
/-- The size of a flattened array of arrays. -/
|
||
def Array.flatSize {α} : Array (Array α) → Nat :=
|
||
foldl (init := 0) fun sizeAcc as => sizeAcc + as.size
|
||
|
||
/-- The size of a flattened array of arrays after applying an element-wise filter. -/
|
||
def Array.flatFilterSizeM {α m} [Monad m] (p : α → m Bool) : Array (Array α) → m Nat :=
|
||
foldlM (init := 0) fun sizeAcc as => return sizeAcc + (← as.filterM p).size
|
||
|
||
open Lean
|
||
|
||
/-!
|
||
|
||
## Imports
|
||
|
||
-/
|
||
|
||
/-- Gets all imports within HepLean. -/
|
||
def HepLean.allImports : IO (Array Import) := do
|
||
initSearchPath (← findSysroot)
|
||
let mods := `HepLean
|
||
let mFile ← findOLean mods
|
||
unless ← mFile.pathExists do
|
||
throw <| IO.userError s!"object file '{mFile}' of module {mods} does not exist"
|
||
let (hepLeanMod, _) ← readModuleData mFile
|
||
hepLeanMod.imports.filterM fun c => return c.module != `Init
|
||
|
||
/-- Number of files within HepLean. -/
|
||
def HepLean.noImports : IO Nat := do
|
||
let imports ← allImports
|
||
return imports.size
|
||
|
||
/-- Gets all constants from an import. -/
|
||
def HepLean.Imports.getConsts (imp : Import) : IO (Array ConstantInfo) := do
|
||
let mFile ← findOLean imp.module
|
||
let (modData, _) ← readModuleData mFile
|
||
return modData.constants
|
||
|
||
/-- Gets all user defined constants from an import. -/
|
||
def HepLean.Imports.getUserConsts (imp : Import) : CoreM (Array ConstantInfo) := do
|
||
let env ← getEnv
|
||
let consts ← Imports.getConsts imp
|
||
consts.filterM fun c =>
|
||
let name := c.name
|
||
return !name.isInternal
|
||
&& !isCasesOnRecursor env name
|
||
&& !isRecOnRecursor env name
|
||
&& !isNoConfusion env name
|
||
&& !isBRecOnRecursor env name
|
||
&& !isAuxRecursorWithSuffix env name binductionOnSuffix
|
||
&& !isAuxRecursorWithSuffix env name belowSuffix
|
||
&& !isAuxRecursorWithSuffix env name ibelowSuffix
|
||
/- Removing syntax category declarations. -/
|
||
&& name.toString != "TensorTree.indexExpr.quot"
|
||
&& name.toString != "TensorTree.tensorExpr.quot"
|
||
|
||
/-- Turns a name into a system file path. -/
|
||
def Lean.Name.toFilePath (c : Name) : System.FilePath :=
|
||
System.mkFilePath (c.toString.splitOn ".") |>.addExtension "lean"
|
||
|
||
/-- Lines from import. -/
|
||
def HepLean.Imports.getLines (imp : Import) : IO (Array String) := do
|
||
IO.FS.lines imp.module.toFilePath
|
||
|
||
namespace Lean.Name
|
||
/-!
|
||
|
||
## Name
|
||
|
||
-/
|
||
|
||
variable {m} [Monad m] [MonadEnv m] [MonadLiftT BaseIO m]
|
||
|
||
/-- Turns a name into a Lean file. -/
|
||
def toRelativeFilePath (c : Name) : System.FilePath :=
|
||
System.FilePath.join "." c.toFilePath
|
||
|
||
/-- Turns a name, which represents a module, into a link to github. -/
|
||
def toGitHubLink (c : Name) (line : Nat) : String :=
|
||
s!"https://github.com/HEPLean/HepLean/blob/master/{c.toFilePath}#L{line}"
|
||
|
||
/-- Given a name, returns the line number. -/
|
||
def lineNumber (c : Name) : m Nat := do
|
||
match ← findDeclarationRanges? c with
|
||
| some decl => return decl.range.pos.line
|
||
| none => panic! s!"{c} is a declaration without position"
|
||
|
||
/-- Given a name, returns the file name corresponding to that declaration. -/
|
||
def fileName (c : Name) : m Name := do
|
||
let env ← getEnv
|
||
match env.getModuleFor? c with
|
||
| some decl => return decl
|
||
| none => panic! s!"{c} is a declaration without position"
|
||
|
||
/-- Returns the location of a name. -/
|
||
def location (c : Name) : m String := do
|
||
let env ← getEnv
|
||
match env.getModuleFor? c with
|
||
| some decl => return s!"{decl.toRelativeFilePath}:{← c.lineNumber} {c}"
|
||
| none => panic! s!"{c} is a declaration without position"
|
||
|
||
/-- Determines if a name has a location. -/
|
||
def hasPos (c : Name) : m Bool := do
|
||
let ranges? ← findDeclarationRanges? c
|
||
return ranges?.isSome
|
||
|
||
/-- Determines if a name has a doc string. -/
|
||
def hasDocString (c : Name) : CoreM Bool := do
|
||
let env ← getEnv
|
||
let doc? ← findDocString? env c
|
||
return doc?.isSome
|
||
|
||
/-- The doc string from a name. -/
|
||
def getDocString (c : Name) : CoreM String := do
|
||
let env ← getEnv
|
||
let doc? ← findDocString? env c
|
||
return doc?.getD ""
|
||
|
||
/-- Given a name, returns the source code defining that name, including doc strings. -/
|
||
def getDeclString (name : Name) : CoreM String := do
|
||
let env ← getEnv
|
||
match ← findDeclarationRanges? name with
|
||
| some { range := { pos, endPos, .. }, .. } =>
|
||
match env.getModuleFor? name with
|
||
| some fileName =>
|
||
let fileContent ← IO.FS.readFile fileName.toRelativeFilePath
|
||
let fileMap := fileContent.toFileMap
|
||
return fileMap.source.extract (fileMap.ofPosition pos) (fileMap.ofPosition endPos)
|
||
| none => return ""
|
||
| none => return ""
|
||
|
||
/-- Given a name, returns the source code defining that name,
|
||
starting with the def ... or lemma... etc. -/
|
||
def getDeclStringNoDoc (name : Name) : CoreM String := do
|
||
let declerationString ← getDeclString name
|
||
let headerLine (line : String) : Bool :=
|
||
line.startsWith "def " ∨ line.startsWith "lemma " ∨ line.startsWith "inductive "
|
||
∨ line.startsWith "structure " ∨ line.startsWith "theorem "
|
||
∨ line.startsWith "instance " ∨ line.startsWith "abbrev " ∨
|
||
line.startsWith "noncomputable def "
|
||
let lines := declerationString.splitOn "\n"
|
||
match lines.findIdx? headerLine with
|
||
| none => panic! s!"{name} has no header line"
|
||
| some i => return String.intercalate "\n" (lines.drop i)
|
||
|
||
end Lean.Name
|
||
|
||
namespace HepLean
|
||
|
||
/-- Number of definitions. -/
|
||
def noDefs : CoreM Nat := do
|
||
let imports ← HepLean.allImports
|
||
let x ← imports.mapM HepLean.Imports.getUserConsts
|
||
x.flatFilterSizeM fun c => return c.isDef && (← c.name.hasPos)
|
||
|
||
/-- Number of definitions. -/
|
||
def noLemmas : CoreM Nat := do
|
||
let imports ← HepLean.allImports
|
||
let x ← imports.mapM HepLean.Imports.getUserConsts
|
||
x.flatFilterSizeM fun c => return !c.isDef && (← c.name.hasPos)
|
||
|
||
/-- All docstrings present in HepLean. -/
|
||
def allDocStrings : CoreM (Array String) := do
|
||
let imports ← HepLean.allImports
|
||
let x ← imports.mapM HepLean.Imports.getUserConsts
|
||
let x' := x.flatten
|
||
let docString ← x'.mapM fun c => Lean.Name.getDocString c.name
|
||
return docString
|
||
|
||
/-- Number of definitions without a doc-string. -/
|
||
def noDefsNoDocString : CoreM Nat := do
|
||
let imports ← HepLean.allImports
|
||
let x ← imports.mapM HepLean.Imports.getUserConsts
|
||
x.flatFilterSizeM fun c =>
|
||
return c.isDef && (← c.name.hasPos) && !(← c.name.hasDocString)
|
||
|
||
/-- Number of definitions without a doc-string. -/
|
||
def noLemmasNoDocString : CoreM Nat := do
|
||
let imports ← HepLean.allImports
|
||
let x ← imports.mapM HepLean.Imports.getUserConsts
|
||
x.flatFilterSizeM fun c =>
|
||
return !c.isDef && (← c.name.hasPos) && !(← c.name.hasDocString)
|
||
|
||
/-- The number of lines in HepLean. -/
|
||
def noLines : IO Nat := do
|
||
let imports ← HepLean.allImports
|
||
let x ← imports.mapM HepLean.Imports.getLines
|
||
return x.flatSize
|
||
|
||
/-- The number of TODO items. -/
|
||
def noTODOs : IO Nat := do
|
||
let imports ← HepLean.allImports
|
||
let x ← imports.mapM HepLean.Imports.getLines
|
||
x.flatFilterSizeM fun l => return l.startsWith "TODO "
|
||
|
||
/-- The number of files with a TODO item. -/
|
||
def noFilesWithTODOs : IO Nat := do
|
||
let imports ← HepLean.allImports
|
||
let x ← imports.mapM HepLean.Imports.getLines
|
||
let x := x.filter fun bs => bs.any fun l => l.startsWith "TODO "
|
||
return x.size
|
||
|
||
end HepLean
|