Skip to content

Commit

Permalink
feat(doc): Add documentation generation
Browse files Browse the repository at this point in the history
Adds the `doc` module to `gluon` which contains functions to generate
markdown from a module or a directory of modules. Also exports this
functionality through the `doc` subcommand
  • Loading branch information
Marwes committed Mar 24, 2018
1 parent 9c9b4e6 commit 1d19993
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 53 deletions.
32 changes: 32 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ either = "1.0.0"
itertools = "0.7.0"
futures = "0.1.11"
walkdir = "1"
failure = { version = "0.1", features = ["backtrace"] }

serde = { version = "1.0.0", optional = true }
serde_state = { version = "0.4.0", optional = true }
Expand Down
12 changes: 6 additions & 6 deletions repl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ fn run() -> std::result::Result<(), Box<std::error::Error + Send + Sync>> {
)
(@subcommand doc =>
(about: "Documents gluon source code")
(@arg INPUT: ... "Documents the file or directory")
(@arg INPUT: +required "Documents the file or directory")
(@arg OUTPUT: +required "Outputs the documentation to this directory")
)
(@arg INPUT: ... "Executes each file as a gluon program")
).get_matches();
Expand Down Expand Up @@ -149,11 +150,10 @@ fn run() -> std::result::Result<(), Box<std::error::Error + Send + Sync>> {
fmt_stdio()?;
}
} else if let Some(fmt_matches) = matches.subcommand_matches("doc") {
if let Some(args) = fmt_matches.values_of("INPUT") {
for arg in args {
gluon::doc::generate_for_path(&new_vm(), arg, "doc")?;
}
}
let input = fmt_matches.value_of("INPUT").expect("INPUT");
let output = fmt_matches.value_of("OUTPUT").expect("OUTPUT");
gluon::doc::generate_for_path(&new_vm(), input, output)
.map_err(|err| format!("{}\n{}", err, err.backtrace()))?;
} else if matches.is_present("REPL") {
repl::run()?;
} else if let Some(args) = matches.values_of("INPUT") {
Expand Down
6 changes: 3 additions & 3 deletions src/compiler_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ impl<'s> MacroExpandable for &'s mut SpannedExpr<Symbol> {
compiler: &mut Compiler,
macros: &mut MacroExpander,
file: &str,
_expr_str: &str,
expr_str: &str,
) -> SalvageResult<MacroValue<Self::Expr>> {
if compiler.implicit_prelude {
if compiler.implicit_prelude && !expr_str.starts_with("//@NO-IMPLICIT-PRELUDE") {
compiler.include_implicit_prelude(macros.vm.global_env().type_cache(), file, self);
}
macros.run(self);
Expand All @@ -138,7 +138,7 @@ impl MacroExpandable for SpannedExpr<Symbol> {
file: &str,
expr_str: &str,
) -> SalvageResult<MacroValue<Self::Expr>> {
if compiler.implicit_prelude {
if compiler.implicit_prelude && !expr_str.starts_with("//@NO-IMPLICIT-PRELUDE") {
compiler.include_implicit_prelude(macros.vm.global_env().type_cache(), file, &mut self);
}
let prev_errors = mem::replace(&mut macros.errors, Errors::new());
Expand Down
125 changes: 82 additions & 43 deletions src/doc.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,73 @@
extern crate failure;
extern crate walkdir;

use std::fmt;
use std::fs::File;
use std::io::{self, Read, Write};
use std::fmt::{self, Write as WriteFmt};
use std::fs::{create_dir_all, File};
use std::io::{Read, Write};
use std::path::Path;

use self::failure::ResultExt;

use base::filename_to_module;
use base::types::ArcType;
use base::metadata::Metadata;
use check::metadata::metadata;
use {new_vm, Compiler, Thread};
use {Compiler, Thread};

quick_error! {
#[derive(Debug)]
pub enum Error {
Io(err: io::Error) {
description(err.description())
display("{}", err)
from()
}
Fmt(err: fmt::Error) {
description(err.description())
display("{}", err)
from()
}
WalkDir(err: walkdir::Error) {
description(err.description())
display("{}", err)
from()
}
pub type Error = failure::Error;
pub type Result<T> = ::std::result::Result<T, Error>;

fn generate_field<W>(
out: &mut W,
field_name: &str,
typ: &ArcType,
comment: Option<&str>,
) -> Result<()>
where
W: ?Sized + fmt::Write,
{
writeln!(out, "### {}", field_name)?;

writeln!(out, "```gluon")?;
writeln!(out, "{}", typ)?;
writeln!(out, "```")?;

if let Some(comment) = comment {
writeln!(out, "{}", comment)?;
}
writeln!(out)?;
Ok(())
}

pub type Result<T> = ::std::result::Result<T, Error>;

pub fn generate<W>(out: &mut W, typ: &ArcType, meta: &Metadata) -> Result<()>
where
W: ?Sized + fmt::Write,
{
for field in typ.row_iter() {
if typ.type_field_iter().next().is_some() {
writeln!(out, "## Types")?;
writeln!(out)?;
}
for field in typ.type_field_iter() {
let field_name: &str = field.name.as_ref();
let meta = meta.module.get(field_name).unwrap();
let field_type = &field.typ;

writeln!(out, "## {}", field_name)?;

writeln!(out, "```gluon")?;
writeln!(out, "{}", field_type)?;
writeln!(out, "```")?;
let field_type = &field.typ.unresolved_type();
let comment = meta.module
.get(field_name)
.and_then(|meta| meta.comment.as_ref().map(|s| &s[..]));
generate_field(out, field_name, field_type, comment)?;
}

if let Some(ref comment) = meta.comment {
writeln!(out, "{}", comment)?;
}
if typ.row_iter().next().is_some() {
writeln!(out, "## Values")?;
writeln!(out)?;
}

generate(out, &field.typ, meta)?;
for field in typ.row_iter() {
let field_name: &str = field.name.as_ref();
let field_type = &field.typ;
let comment = meta.module
.get(field_name)
.and_then(|meta| meta.comment.as_ref().map(|s| &s[..]));
generate_field(out, field_name, field_type, comment)?;
}
Ok(())
}
Expand All @@ -64,21 +79,45 @@ where
{
for entry in walkdir::WalkDir::new(path) {
let entry = entry?;
if !entry.file_type().is_file() {
if !entry.file_type().is_file()
|| entry.path().extension().and_then(|ext| ext.to_str()) != Some("glu")
{
continue;
}
let mut input = File::open(entry.path())?;
let mut input = File::open(&*entry.path()).with_context(|err| {
format!(
"Unable to open gluon file `{}`: {}",
entry.path().display(),
err
)
})?;
let mut content = String::new();
input.read_to_string(&mut content)?;

let (expr, typ) = Compiler::new()
.typecheck_str(thread, "basic", &content, None)
.unwrap();
let (expr, typ) = Compiler::new().typecheck_str(thread, "basic", &content, None)?;
let (meta, _) = metadata(&*thread.get_env(), &expr);

let mut out = String::new();
if let Some(module_path) = entry.path().to_str() {
writeln!(out, "# {}", filename_to_module(module_path))?;
writeln!(out)?;
}
generate(&mut out, &typ, &meta)?;
let mut doc_file = File::create(out_path.as_ref().join(entry.path().with_extension("md")))?;

create_dir_all(
out_path
.as_ref()
.join(entry.path().parent().unwrap_or(Path::new(""))),
)?;

let out_path = out_path.as_ref().join(entry.path().with_extension("md"));
let mut doc_file = File::create(&*out_path).with_context(|err| {
format!(
"Unable to open output file `{}`: {}",
out_path.display(),
err
)
})?;
doc_file.write_all(out.as_bytes())?;
}
Ok(())
Expand Down
2 changes: 1 addition & 1 deletion std/list.glu
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ let monad : Monad List =

{ applicative = applicative, flat_map }

let show ?d: [Show a] -> Show (List a) =
let show ?d : [Show a] -> Show (List a) =
let (++) = string.semigroup.append

{
Expand Down

0 comments on commit 1d19993

Please sign in to comment.