|
| 1 | +import REPL.JSON |
| 2 | + |
| 3 | +open Lean Elab System Parser |
| 4 | + |
| 5 | +namespace REPL |
| 6 | + |
| 7 | +/-! Extract information about declarations from info trees |
| 8 | +Inspired by <https://github.com/frenzymath/jixia> |
| 9 | +-/ |
| 10 | + |
| 11 | +/-- See `Lean.Parser.Command.declModifiers` and `Lean.Elab.elabModifiers` -/ |
| 12 | +def getModifiers (stx : Syntax) (ctx: ContextInfo): DeclModifiers := |
| 13 | + match stx with |
| 14 | + | .node _ ``Command.declModifiers args => |
| 15 | + { docString := stx[0].getOptional?.map (fun stx => |
| 16 | + { content := stx.prettyPrint.pretty, range := stx.toRange ctx }), |
| 17 | + visibility := (stx[2].getOptional?.map (·.prettyPrint.pretty.trim)).getD "regular", |
| 18 | + computeKind := (stx[3].getOptional?.map (·.prettyPrint.pretty.trim)).getD "regular", |
| 19 | + recKind := (stx[5].getOptional?.map (·.prettyPrint.pretty.trim)).getD "default", |
| 20 | + isUnsafe := !stx[4].isNone, |
| 21 | + attributes := stx[1].getArgs.toList.flatMap parseAttributes, } |
| 22 | + | _ => {} |
| 23 | + where |
| 24 | + /-- Parse attribute instances from a Term.attributes syntax node |
| 25 | + See `Lean.Parser.Term.attributes`. |
| 26 | + -/ |
| 27 | + parseAttributes (attrSyntax : Syntax) : List String := |
| 28 | + match attrSyntax with |
| 29 | + | .node _ ``Term.attributes args => |
| 30 | + -- args[0] is "@[", args[1] is the attribute list, args[2] is "]" |
| 31 | + if args.size > 1 then |
| 32 | + args[1]!.getArgs.toList.flatMap fun inst => |
| 33 | + match inst with |
| 34 | + | .node _ ``Term.attrInstance _ => [inst.prettyPrint.pretty.trim] |
| 35 | + | _ => [] |
| 36 | + else [] |
| 37 | + | _ => [] |
| 38 | + |
| 39 | +partial def getIdents (stx : Syntax) : Array Name := |
| 40 | + match stx with |
| 41 | + | .node _ _ args => args.flatMap getIdents |
| 42 | + | .ident _ _ id _ => #[id] |
| 43 | + | _ => #[] |
| 44 | + |
| 45 | +/-- See `Lean.Elab.Term.toBinderViews` -/ |
| 46 | +def toBinderViews (stx : Syntax) : Array BinderView := |
| 47 | + let k := stx.getKind |
| 48 | + if stx.isIdent || k == ``Term.hole then |
| 49 | + -- binderIdent |
| 50 | + #[{ id := (expandBinderIdent stx), type := mkHole stx, binderInfo := "default" }] |
| 51 | + else if k == ``Lean.Parser.Term.explicitBinder then |
| 52 | + -- `(` binderIdent+ binderType (binderDefault <|> binderTactic)? `)` |
| 53 | + let ids := getBinderIds stx[1] |
| 54 | + let type := stx[2] |
| 55 | + let optModifier := stx[3] |
| 56 | + ids.map fun id => { id := (expandBinderIdent id), type := (expandBinderType id type), binderInfo := "default" } |
| 57 | + else if k == ``Lean.Parser.Term.implicitBinder then |
| 58 | + -- `{` binderIdent+ binderType `}` |
| 59 | + let ids := getBinderIds stx[1] |
| 60 | + let type := stx[2] |
| 61 | + ids.map fun id => { id := (expandBinderIdent id), type := (expandBinderType id type), binderInfo := "implicit" } |
| 62 | + else if k == ``Lean.Parser.Term.strictImplicitBinder then |
| 63 | + -- `⦃` binderIdent+ binderType `⦄` |
| 64 | + let ids := getBinderIds stx[1] |
| 65 | + let type := stx[2] |
| 66 | + ids.map fun id => { id := (expandBinderIdent id), type := (expandBinderType id type), binderInfo := "strictImplicit" } |
| 67 | + else if k == ``Lean.Parser.Term.instBinder then |
| 68 | + -- `[` optIdent type `]` |
| 69 | + let id := expandOptIdent stx[1] |
| 70 | + let type := stx[2] |
| 71 | + #[ { id := id, type := type, binderInfo := "instImplicit" } ] |
| 72 | + else |
| 73 | + #[] |
| 74 | + where |
| 75 | + getBinderIds (ids : Syntax) : Array Syntax := |
| 76 | + ids.getArgs.map fun (id : Syntax) => |
| 77 | + let k := id.getKind |
| 78 | + if k == identKind || k == `Lean.Parser.Term.hole then id |
| 79 | + else Syntax.missing |
| 80 | + expandBinderType (ref : Syntax) (stx : Syntax) : Syntax := |
| 81 | + if stx.getNumArgs == 0 then mkHole ref else stx[1] |
| 82 | + expandBinderIdent (stx : Syntax) : Syntax := |
| 83 | + match stx with |
| 84 | + | `(_) => Syntax.missing |
| 85 | + | _ => stx |
| 86 | + expandOptIdent (stx : Syntax) : Syntax := |
| 87 | + if stx.isNone then Syntax.missing else stx[0] |
| 88 | + |
| 89 | +def getScope (ctx : ContextInfo) (state : Command.State) : ScopeInfo := |
| 90 | + let scope := state.scopes.head! |
| 91 | + { |
| 92 | + varDecls := scope.varDecls.map fun stx => s!"variable {stx.raw.prettyPrint.pretty}", |
| 93 | + includeVars := scope.includedVars.toArray.map fun name => name.eraseMacroScopes, |
| 94 | + omitVars := scope.omittedVars.toArray.map fun name => name.eraseMacroScopes, |
| 95 | + levelNames := scope.levelNames.toArray, |
| 96 | + currNamespace := ctx.currNamespace, |
| 97 | + openDecl := ctx.openDecls, |
| 98 | + } |
| 99 | + |
| 100 | +partial def extractDeclarationInfo (cmdInfo : CommandInfo) (infoTree : InfoTree) (ctx : ContextInfo) |
| 101 | + (prevState : Command.State) : DeclarationInfo := |
| 102 | + let stx := cmdInfo.stx |
| 103 | + let modifiers := getModifiers stx[0] ctx |
| 104 | + let decl := stx[1] |
| 105 | + let kind := decl.getKind |
| 106 | + let kindStr := match kind with |
| 107 | + | .str _ s => s |
| 108 | + | _ => kind.toString |
| 109 | + |
| 110 | + -- See `Lean.Elab.DefView` |
| 111 | + let (signature, id, binders, type?, value?) := match kind with |
| 112 | + | ``Command.abbrev |
| 113 | + | ``Command.definition => |
| 114 | + let (binders, type) := expandOptDeclSig decl[2] |
| 115 | + (decl[2], decl[1], binders, type, some decl[3]) |
| 116 | + | ``Command.theorem => |
| 117 | + let (binders, type) := expandDeclSig decl[2] |
| 118 | + (decl[2], decl[1], binders, some type, some decl[3]) |
| 119 | + | ``Command.opaque => |
| 120 | + let (binders, type) := expandDeclSig decl[2] |
| 121 | + (decl[2], decl[1], binders, some type, decl[3].getOptional?) |
| 122 | + | ``Command.axiom => |
| 123 | + let (binders, type) := expandDeclSig decl[2] |
| 124 | + (decl[2], decl[1], binders, some type, none) |
| 125 | + | ``Command.inductive |
| 126 | + | ``Command.classInductive => |
| 127 | + let (binders, type) := expandOptDeclSig decl[2] |
| 128 | + (decl[2], decl[1], binders, type, some decl[4]) |
| 129 | + | ``Command.instance => |
| 130 | + let (binders, type) := expandDeclSig decl[4] |
| 131 | + let declId := match stx[3].getOptional? with |
| 132 | + | some declId => declId |
| 133 | + | none => Syntax.missing -- TODO: improve this |
| 134 | + (decl[4], declId, binders, some type, some decl[5]) |
| 135 | + | ``Command.example => |
| 136 | + let id := mkIdentFrom stx[0] `_example (canonical := true) |
| 137 | + let declId := mkNode ``Parser.Command.declId #[id, mkNullNode] |
| 138 | + let (binders, type) := expandOptDeclSig decl[1] |
| 139 | + (decl[1], declId, binders, type, some decl[2]) |
| 140 | + | ``Command.structure => |
| 141 | + let signature := Syntax.node2 .none ``Command.optDeclSig decl[2] decl[4] |
| 142 | + let (binders, type) := (decl[2], some decl[4]) |
| 143 | + (signature, decl[1], binders, type, none) |
| 144 | + | _ => unreachable! |
| 145 | + |
| 146 | + let name := id[0].getId |
| 147 | + let fullName := match getFullname infoTree name with -- TODO: could be better |
| 148 | + | [] => name |
| 149 | + | a :: _ => a |
| 150 | + |
| 151 | + let binderViews := binders.getArgs.flatMap toBinderViews |
| 152 | + let binders : Option DeclBinders := match binders.getArgs with |
| 153 | + | #[] => none |
| 154 | + | _ => some { pp := binders.prettyPrint.pretty, |
| 155 | + groups := binders.getArgs.map (·.prettyPrint.pretty), |
| 156 | + map := binders.getArgs.flatMap toBinderViews, |
| 157 | + range := binders.toRange ctx } |
| 158 | + |
| 159 | + let a := prevState.env.constants.find! decl[1].getId |
| 160 | + -- a.getUsedConstantsAsSet |
| 161 | + |
| 162 | + let extractConstants (stx : Syntax) : Array Name := -- TODO: improve this |
| 163 | + let idents := ((getIdents stx).foldl |
| 164 | + (init := NameSet.empty) fun acc name => acc.insert name).toArray |
| 165 | + idents |
| 166 | + -- idents.filter prevState.env.constants.contains |
| 167 | + |
| 168 | + { |
| 169 | + pp := stx.prettyPrint.pretty, |
| 170 | + name, |
| 171 | + fullName, |
| 172 | + kind := kindStr, |
| 173 | + modifiers, |
| 174 | + scope := getScope ctx prevState, |
| 175 | + signature := { pp := signature.prettyPrint.pretty, |
| 176 | + constants := extractConstants signature, |
| 177 | + range := signature.toRange ctx }, |
| 178 | + binders, |
| 179 | + type := type?.map fun t => |
| 180 | + { pp := t.prettyPrint.pretty, constants := extractConstants t, range := t.toRange ctx }, |
| 181 | + value := value?.map fun v => |
| 182 | + { pp := v.prettyPrint.pretty, constants := extractConstants v, range := v.toRange ctx }, |
| 183 | + range := stx.toRange ctx |
| 184 | + } |
| 185 | +where |
| 186 | + getFullname (infoTree : InfoTree) (shortName : Name) : List Name := |
| 187 | + match infoTree with |
| 188 | + | .context _ t => getFullname t shortName |
| 189 | + | .node i ts => |
| 190 | + match i with |
| 191 | + | .ofTermInfo ti => ti.expr.constName?.toList.filter (fun n => |
| 192 | + match shortName.componentsRev with |
| 193 | + | [] => true |
| 194 | + | h :: _ => match n.componentsRev with |
| 195 | + | [] => false |
| 196 | + | h' :: _ => h == h' |
| 197 | + ) |
| 198 | + | _ => ts.toList.flatMap (getFullname · shortName) |
| 199 | + | _ => [] |
| 200 | + |
| 201 | +open Lean.Parser in |
| 202 | +/-- Extract all declarations from InfoTrees -/ |
| 203 | +partial def extractCmdDeclarationInfos (trees : List InfoTree) (prevState : Command.State) : |
| 204 | + List DeclarationInfo := |
| 205 | + let allDecls := trees.map (extractFromTree · none prevState) |
| 206 | + allDecls.flatten |
| 207 | +where |
| 208 | + extractFromTree (t : InfoTree) (ctx? : Option ContextInfo) (prevState : Command.State) : |
| 209 | + List DeclarationInfo := |
| 210 | + match t with |
| 211 | + | .context ctx t => extractFromTree t (ctx.mergeIntoOuter? ctx?) prevState |
| 212 | + | .node i ts => |
| 213 | + match i with |
| 214 | + | .ofCommandInfo cmdInfo => |
| 215 | + match ctx? with |
| 216 | + | some ctx => |
| 217 | + if cmdInfo.stx.getKind == ``Command.declaration then |
| 218 | + [extractDeclarationInfo cmdInfo t ctx prevState] |
| 219 | + else |
| 220 | + ts.toList.flatMap (extractFromTree · ctx? prevState) |
| 221 | + | none => ts.toList.flatMap (extractFromTree · ctx? prevState) |
| 222 | + | _ => ts.toList.flatMap (extractFromTree · ctx? prevState) |
| 223 | + | _ => [] |
| 224 | + |
| 225 | +def extractAllDeclarationInfos (treeList : List (IncrementalState × Option InfoTree)) (prevState : Command.State) : List DeclarationInfo := |
| 226 | + match treeList with |
| 227 | + | [] => [] |
| 228 | + | (state, infoTree?) :: rest => |
| 229 | + let decls := extractCmdDeclarationInfos infoTree?.toList prevState |
| 230 | + let restDecls := extractAllDeclarationInfos rest state.commandState |
| 231 | + decls ++ restDecls |
0 commit comments