Skip to content

Commit 5f0bb4f

Browse files
committed
feat(tactic): expose module docstrings as list
1 parent 3bdf1a0 commit 5f0bb4f

File tree

10 files changed

+91
-40
lines changed

10 files changed

+91
-40
lines changed

doc/changes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ This is the first community release of Lean 3.
3131

3232
* Module documentation is now stored in .olean files to allow documentation to be automatically generated. (#54)
3333

34+
* Documentation of all imported modules is now exposed via the `olean_doc_strings` tactic. (#81)
35+
3436
*Bug Fixes*
3537

3638
* build: fix emscripten build in travis (#68)

library/init/meta/tactic.lean

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,43 @@ It updates the environment in the tactic_state, and returns an expression of the
543543
where l_i's and a_j's are the collected dependencies.
544544
-/
545545
meta constant add_aux_decl (c : name) (type : expr) (val : expr) (is_lemma : bool) : tactic expr
546-
meta constant module_doc_strings : tactic (list (option string × string))
546+
547+
/-- Returns a list of all top-level (`/-!`) docstrings in the active module and imported ones.
548+
The returned object is a list of modules, indexed by `(some filename)` for imported modules
549+
and `none` for the active one, where each module in the list is paired with a list
550+
of `(position_in_file, docstring)` pairs. -/
551+
meta constant olean_doc_strings : tactic (list (option string × (list (pos × string))))
552+
553+
/-- Returns a list of docstrings in the active module. An entry in the list can be either:
554+
- a top-level (`/-!`) docstring, represented as `(none, docstring)`
555+
- a declaration-specific (`/--`) docstring, represented as `(some decl_name, docstring)` -/
556+
meta def module_doc_strings : tactic (list (option name × string)) :=
557+
do
558+
/- Obtain a list of top-level docs in current module. -/
559+
mod_docs ← olean_doc_strings,
560+
let mod_docs: list (option name × list string) :=
561+
mod_docs.filter_map (λ d,
562+
if d.1.is_none
563+
then some ⟨none, d.2.map prod.snd⟩
564+
else none),
565+
/- Transform them to the expected format. -/
566+
let mod_docs :=
567+
mod_docs.map (λ d,
568+
d.2.map (λ doc, (d.1, doc))),
569+
let mod_docs := mod_docs.join,
570+
/- Obtain list of declarations in current module. -/
571+
e ← get_env,
572+
let decls := environment.fold e ([]: list name)
573+
(λ d acc, let n := d.to_name in
574+
if (environment.decl_olean e n).is_none
575+
then n::acc else acc),
576+
/- Map declarations to those which have docstrings. -/
577+
decls ← decls.mfoldl (λa n,
578+
(doc_string n >>=
579+
λ doc, pure $ (some n, doc) :: a)
580+
<|> pure a) [],
581+
pure (mod_docs ++ decls)
582+
547583
/-- Set attribute `attr_name` for constant `c_name` with the given priority.
548584
If the priority is none, then use default -/
549585
meta constant set_basic_attribute (attr_name : name) (c_name : name) (persistent := ff) (prio : option nat := none) : tactic unit

src/frontends/lean/parser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2402,7 +2402,7 @@ std::string parser::parse_doc_block() {
24022402
}
24032403

24042404
void parser::parse_mod_doc_block() {
2405-
m_env = add_module_doc_string(m_env, m_scanner.get_str_val());
2405+
m_env = add_module_doc_string(m_env, m_scanner.get_str_val(), m_scanner.get_pos_info() );
24062406
next();
24072407
}
24082408

src/library/documentation.cpp

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ Author: Leonardo de Moura
88
#include <algorithm>
99
#include <functional>
1010
#include <cctype>
11+
#include <utility>
12+
#include <vector>
1113
#include "util/sstream.h"
1214
#include "library/module.h"
1315
#include "library/documentation.h"
1416

1517
namespace lean {
1618
struct documentation_ext : public environment_extension {
17-
/** Doc string for the current module being processed. It does not include imported doc strings. */
18-
optional<std::string> m_module_doc;
19+
/** Doc strings for the current module being processed. It does not include imported doc strings. */
20+
std::vector<std::pair<pos_info, std::string>> m_module_docs;
1921
/** Doc strings for declarations (including imported ones). We store doc_strings for declarations in the .olean files. */
2022
name_map<std::string> m_doc_string_map;
2123
};
@@ -167,16 +169,12 @@ static std::string process_doc(std::string s) {
167169
return add_lean_suffix_to_code_blocks(s);
168170
}
169171

170-
environment add_module_doc_string(environment const & env, std::string doc) {
172+
environment add_module_doc_string(environment const & env, std::string doc, pos_info pos) {
171173
doc = process_doc(doc);
172174
auto ext = get_extension(env);
173-
if (ext.m_module_doc) {
174-
*ext.m_module_doc += "\n" + doc;
175-
} else {
176-
ext.m_module_doc = doc;
177-
}
175+
ext.m_module_docs.emplace_back(pos, doc);
178176
auto new_env = update(env, ext);
179-
return module::add_doc_string(new_env, doc);
177+
return module::add_doc_string(new_env, doc, pos);
180178
}
181179

182180
environment add_doc_string(environment const & env, name const & n, std::string doc) {
@@ -204,9 +202,7 @@ void get_module_doc_strings(environment const & env, buffer<mod_doc_entry> & res
204202
for (auto const & pr : mod_docs) {
205203
result.push_back({ optional<std::string>{ pr.first }, pr.second });
206204
}
207-
if (ext.m_module_doc) {
208-
result.push_back({ {}, *ext.m_module_doc });
209-
}
205+
result.push_back({ {}, ext.m_module_docs });
210206
}
211207

212208
void initialize_documentation() {

src/library/documentation.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ Author: Leonardo de Moura
66
*/
77
#pragma once
88
#include <string>
9+
#include <utility>
10+
#include <vector>
911
#include "kernel/environment.h"
12+
#include "util/message_definitions.h"
13+
1014
namespace lean {
1115
struct mod_doc_entry {
1216
optional<std::string> m_mod_name;
13-
std::string m_doc;
17+
std::vector<std::pair<pos_info, std::string>> m_docs;
1418
};
15-
environment add_module_doc_string(environment const & env, std::string doc);
19+
environment add_module_doc_string(environment const & env, std::string doc, pos_info pos);
1620
environment add_doc_string(environment const & env, name const & n, std::string);
1721
optional<std::string> get_doc_string(environment const & env, name const & n);
1822
void get_module_doc_strings(environment const & env, buffer<mod_doc_entry> & result);

src/library/module.cpp

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ struct module_ext : public environment_extension {
9090
* of module different from that of a source file, so we use file names to index
9191
* the docstring map. */
9292
// TODO(Vtec234): lean::rb_map misbehaves here for some reason
93-
std::map<std::string, std::string> m_module_docs;
93+
std::unordered_map<std::string, std::vector<std::pair<pos_info, std::string>>> m_module_docs;
9494
// Map from declaration name to olean file where it was defined
9595
name_map<std::string> m_decl2olean;
9696
name_map<pos_info> m_decl2pos_info;
@@ -425,23 +425,25 @@ struct mod_doc_modification : public modification {
425425
LEAN_MODIFICATION("mod_doc")
426426

427427
std::string m_doc;
428+
pos_info m_pos;
428429

429430
mod_doc_modification() {}
430-
/** A docstring for a declaration in the module. */
431-
mod_doc_modification(std::string const & doc) : m_doc(doc) {}
431+
/** A module-level docstring. */
432+
mod_doc_modification(std::string const & doc, pos_info pos) : m_doc(doc), m_pos(pos) {}
432433

433434
void perform(environment & env) const override {
434435
// No-op. See `import_module` for actual action
435436
}
436437

437438
void serialize(serializer & s) const override {
438-
s << m_doc;
439+
s << m_doc << m_pos;
439440
}
440441

441442
static std::shared_ptr<modification const> deserialize(deserializer & d) {
442443
std::string doc;
443-
d >> doc;
444-
return std::make_shared<mod_doc_modification>(doc);
444+
pos_info pos;
445+
d >> doc >> pos;
446+
return std::make_shared<mod_doc_modification>(doc, pos);
445447
}
446448
};
447449

@@ -535,11 +537,11 @@ environment add(environment const & env, certified_declaration const & d) {
535537
return add_decl_pos_info(new_env, _d.get_name());
536538
}
537539

538-
environment add_doc_string(environment const & env, std::string const & doc) {
539-
return add(env, std::make_shared<mod_doc_modification>(doc));
540+
environment add_doc_string(environment const & env, std::string const & doc, pos_info pos) {
541+
return add(env, std::make_shared<mod_doc_modification>(doc, pos));
540542
}
541543

542-
std::map<std::string, std::string> const & get_doc_strings(environment const & env) {
544+
std::unordered_map<std::string, std::vector<std::pair<pos_info, std::string>>> const & get_doc_strings(environment const & env) {
543545
return get_extension(env).m_module_docs;
544546
}
545547

@@ -732,11 +734,7 @@ void import_module(modification_list const & modifications, std::string const &
732734
env = add_decl_olean(env, im->m_decl.get_decl().m_name, file_name);
733735
} else if (auto mdm = dynamic_cast<mod_doc_modification const *>(m.get())) {
734736
auto ext = get_extension(env);
735-
if (ext.m_module_docs.count(file_name) != 0) {
736-
ext.m_module_docs[file_name] += "\n" + mdm->m_doc;
737-
} else {
738-
ext.m_module_docs[file_name] = mdm->m_doc;
739-
}
737+
ext.m_module_docs[file_name].emplace_back(mdm->m_pos, mdm->m_doc);
740738
env = update(env, ext);
741739
}
742740
}

src/library/module.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ Released under Apache 2.0 license as described in the file LICENSE.
55
Author: Leonardo de Moura
66
*/
77
#pragma once
8+
#include <unordered_map>
89
#include <string>
910
#include <iostream>
1011
#include <utility>
1112
#include <vector>
12-
#include <map>
1313
#include "util/serializer.h"
1414
#include "util/optional.h"
1515
#include "kernel/pos_info_provider.h"
@@ -145,10 +145,10 @@ environment add_and_perform(environment const & env, std::shared_ptr<modificatio
145145
environment add(environment const & env, certified_declaration const & d);
146146

147147
/** \brief Adds a module-level doc to the current module. */
148-
environment add_doc_string(environment const & env, std::string const & doc);
148+
environment add_doc_string(environment const & env, std::string const & doc, pos_info pos);
149149

150150
/** \brief Returns the map of module-level docs indexed by source file name. */
151-
std::map<std::string, std::string> const & get_doc_strings(environment const & env);
151+
std::unordered_map<std::string, std::vector<std::pair<pos_info, std::string>>> const & get_doc_strings(environment const & env);
152152

153153
/** \brief Return true iff \c n is a definition added to the current module using #module::add */
154154
bool is_definition(environment const & env, name const & n);

src/library/tactic/tactic_state.cpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -758,8 +758,8 @@ vm_obj tactic_add_doc_string(vm_obj const & n, vm_obj const & doc, vm_obj const
758758
}
759759
}
760760

761-
/* meta constant module_doc_strings : tactic (list (option string × string)) */
762-
vm_obj tactic_module_doc_strings(vm_obj const & _s) {
761+
/* meta constant olean_doc_strings : tactic (list (option string × (list (pos × string)))) */
762+
vm_obj tactic_olean_doc_strings(vm_obj const & _s) {
763763
tactic_state const & s = tactic::to_state(_s);
764764
buffer<mod_doc_entry> entries;
765765
get_module_doc_strings(s.env(), entries);
@@ -772,8 +772,18 @@ vm_obj tactic_module_doc_strings(vm_obj const & _s) {
772772
decl_name = mk_vm_some(to_obj(*d));
773773
else
774774
decl_name = mk_vm_none();
775-
vm_obj doc = to_obj(entries[i].m_doc);
776-
vm_obj e = mk_vm_pair(decl_name, doc);
775+
vm_obj lst = mk_vm_simple(0);
776+
unsigned j = entries[i].m_docs.size();
777+
while (j > 0) {
778+
--j;
779+
auto const& doc = entries[i].m_docs[j];
780+
vm_obj line = mk_vm_nat(doc.first.first);
781+
vm_obj column = mk_vm_nat(doc.first.second);
782+
vm_obj pos = mk_vm_constructor(0, line, column); // pos_info
783+
vm_obj doc_obj = mk_vm_pair(pos, to_obj(doc.second));
784+
lst = mk_vm_constructor(1, doc_obj, lst);
785+
}
786+
vm_obj e = mk_vm_pair(decl_name, lst);
777787
r = mk_vm_constructor(1, e, r);
778788
}
779789
return tactic::mk_success(r, s);
@@ -1065,7 +1075,7 @@ void initialize_tactic_state() {
10651075
DECLARE_VM_BUILTIN(name({"tactic", "set_env"}), tactic_set_env);
10661076
DECLARE_VM_BUILTIN(name({"tactic", "doc_string"}), tactic_doc_string);
10671077
DECLARE_VM_BUILTIN(name({"tactic", "add_doc_string"}), tactic_add_doc_string);
1068-
DECLARE_VM_BUILTIN(name({"tactic", "module_doc_strings"}), tactic_module_doc_strings);
1078+
DECLARE_VM_BUILTIN(name({"tactic", "olean_doc_strings"}), tactic_olean_doc_strings);
10691079
DECLARE_VM_BUILTIN(name({"tactic", "open_namespaces"}), tactic_open_namespaces);
10701080
DECLARE_VM_BUILTIN(name({"tactic", "decl_name"}), tactic_decl_name);
10711081
DECLARE_VM_BUILTIN(name({"tactic", "add_aux_decl"}), tactic_add_aux_decl);

tests/lean/doc_strings.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ open tactic
1414
run_cmd doc_string `foo.a >>= trace
1515
run_cmd doc_string `foo.b >>= trace
1616
run_cmd doc_string `two >>= trace
17+
run_cmd olean_doc_strings >>= trace
1718
run_cmd module_doc_strings >>= trace
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
Makes foo.
22
Makes foo of foo.
33
Doesn't make foo.
4-
[(none, This module has a doc..
5-
..or two.)]
4+
[(none, [(⟨1, 0⟩, This module has a doc..), (⟨2, 0⟩, ..or two.)])]
5+
[(none, This module has a doc..),
6+
(none, ..or two.),
7+
((some foo.b), Makes foo of foo.),
8+
((some two), Doesn't make foo.),
9+
((some foo.a), Makes foo.)]

0 commit comments

Comments
 (0)