Skip to content

Commit

Permalink
Add command for generating documentation
Browse files Browse the repository at this point in the history
This adds the command "inko doc", producing a set of JSON files
containing documentation about modules and their symbols. These files
can then be used by a separate tool to generate a website, manual pages,
and so on.

This fixes #333.

Changelog: added
  • Loading branch information
yorickpeterse committed Jun 4, 2024
1 parent 09f9818 commit a3fd841
Show file tree
Hide file tree
Showing 25 changed files with 1,884 additions and 237 deletions.
3 changes: 3 additions & 0 deletions ast/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,9 @@ impl Parser {
TokenKind::Let => ClassExpression::DefineField(Box::new(
self.define_field(token)?,
)),
TokenKind::Comment => {
ClassExpression::Comment(self.comment(token))
}
_ => {
error!(
token.location,
Expand Down
69 changes: 58 additions & 11 deletions compiler/src/compiler.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::config::{BuildDirectories, Output};
use crate::config::{Config, SOURCE, SOURCE_EXT, TESTS};
use crate::docs::{DefineDocumentation, GenerateDocumentation};
use crate::hir;
use crate::linker::link;
use crate::llvm;
Expand Down Expand Up @@ -54,6 +55,7 @@ fn module_name_from_path(config: &Config, file: &Path) -> ModuleName {

pub(crate) fn all_source_modules(
config: &Config,
include_tests: bool,
) -> Result<Vec<(ModuleName, PathBuf)>, String> {
let mut modules = Vec::new();
let mut paths = Vec::new();
Expand All @@ -65,7 +67,7 @@ pub(crate) fn all_source_modules(
paths.push(source.clone());
}

if tests.is_dir() {
if include_tests && tests.is_dir() {
paths.push(tests.clone());
}

Expand Down Expand Up @@ -182,12 +184,15 @@ impl Compiler {

vec![(module_name_from_path(&self.state.config, &file), file)]
} else {
all_source_modules(&self.state.config)
all_source_modules(&self.state.config, true)
.map_err(CompileError::Internal)?
};

let ast = self.parse(input);
let hir = self.compile_hir(ast)?;
let mut hir = self.compile_hir(ast)?;

self.check_types(&mut hir)?;

let res = self.compile_mir(hir).map(|_| ());

self.timings.total = start.elapsed();
Expand All @@ -202,7 +207,10 @@ impl Compiler {
let file = self.main_module_path(file)?;
let main_mod = self.state.db.main_module().unwrap().clone();
let ast = self.parse(vec![(main_mod, file.clone())]);
let hir = self.compile_hir(ast)?;
let mut hir = self.compile_hir(ast)?;

self.check_types(&mut hir)?;

let mut mir = self.compile_mir(hir)?;

self.optimise_mir(&mut mir);
Expand All @@ -221,6 +229,41 @@ impl Compiler {
res
}

pub fn document(
&mut self,
file: Option<PathBuf>,
private: bool,
) -> Result<(), CompileError> {
let input = if let Some(file) = file {
let file = file.canonicalize().unwrap_or(file);

vec![(module_name_from_path(&self.state.config, &file), file)]
} else {
// When generating documentation we don't include the unit tests.
all_source_modules(&self.state.config, false)
.map_err(CompileError::Internal)?
};

let ast = ModulesParser::with_documentation_comments(&mut self.state)
.run(input);
let mut hir = self.compile_hir(ast)?;

self.check_types(&mut hir)?;

// The MIR passes take ownership of the HIR, so we run this first.
DefineDocumentation::run_all(&mut self.state, &mut hir);
self.compile_mir(hir).map(|_| ())?;

let dirs = BuildDirectories::new(&self.state.config);

dirs.create()
.and_then(|_| dirs.create_documentation())
.and_then(|_| {
GenerateDocumentation::run_all(&self.state, &dirs, private)
})
.map_err(CompileError::Internal)
}

pub fn print_diagnostics(&self) {
self.state.config.presenter.present(&self.state.diagnostics);
}
Expand Down Expand Up @@ -325,12 +368,8 @@ LLVM module timings:

fn compile_mir(
&mut self,
mut modules: Vec<hir::Module>,
modules: Vec<hir::Module>,
) -> Result<Mir, CompileError> {
if !self.check_types(&mut modules) {
return Err(CompileError::Invalid);
}

let start = Instant::now();
let mut mir = Mir::new();
let state = &mut self.state;
Expand Down Expand Up @@ -385,7 +424,10 @@ LLVM module timings:
}
}

fn check_types(&mut self, modules: &mut Vec<hir::Module>) -> bool {
fn check_types(
&mut self,
modules: &mut Vec<hir::Module>,
) -> Result<(), CompileError> {
let state = &mut self.state;
let start = Instant::now();
let res = DefineTypes::run_all(state, modules)
Expand All @@ -409,7 +451,12 @@ LLVM module timings:
&& Expressions::run_all(state, modules);

self.timings.type_check = start.elapsed();
res

if res {
Ok(())
} else {
Err(CompileError::Invalid)
}
}

fn optimise_mir(&mut self, mir: &mut Mir) {
Expand Down
20 changes: 17 additions & 3 deletions compiler/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ pub(crate) struct BuildDirectories {

/// The directory to write DOT files to.
pub(crate) dot: PathBuf,

/// The directory to store documentation files in.
pub(crate) documentation: PathBuf,
}

impl BuildDirectories {
Expand All @@ -88,15 +91,22 @@ impl BuildDirectories {
config.build.join(config.target.to_string())
};

let build =
config.opt.directory_name().map(|p| root.join(p)).unwrap_or(root);
let build = config
.opt
.directory_name()
.map(|p| root.join(p))
.unwrap_or_else(|| root.clone());

let objects = build.join("objects");
let llvm_ir = build.join("llvm");
let dot = build.join("dot");
let bin = build.clone();

BuildDirectories { build, objects, llvm_ir, bin, dot }
// The documentation isn't specific to the optimization level used, so
// we always store it in the base build directory.
let documentation = root.join("docs");

BuildDirectories { build, objects, llvm_ir, bin, dot, documentation }
}

pub(crate) fn create(&self) -> Result<(), String> {
Expand All @@ -113,6 +123,10 @@ impl BuildDirectories {
create_directory(&self.dot)
}

pub(crate) fn create_documentation(&self) -> Result<(), String> {
create_directory(&self.documentation)
}

pub(crate) fn create_llvm(&self) -> Result<(), String> {
create_directory(&self.llvm_ir)
}
Expand Down
Loading

0 comments on commit a3fd841

Please sign in to comment.