Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions docs/usage/commands/compile.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ jo compile --python|--ruby|--js [--sast <dir>] <file.jo>... \
[--lib <dir>]... [--link-lib <dir>]... [--link <src>=<tgt>]... -o <output>

# Generate documentation from source files (experimental)
jo compile --doc [--out <dir>] [--title <name>] [--readme <file>] \
[--include-private] [--include-source] <file.jo>...
jo compile --doc [--format html|json] [--query <selectors>] [--out <dir>] \
[--title <name>] [--readme <file>] [--include-private] \
[--include-source] [<file.jo>...]
```

Without a backend flag, the compiler type-checks only. With a backend flag, it produces an executable or script. `--sast <dir>` is optional in both cases — if present, `.sast` files are written to `<dir>` alongside the primary output.
Expand Down Expand Up @@ -44,12 +45,24 @@ Experimental.
| Flag | Description |
|------|-------------|
| `--doc` | Generate documentation instead of normal compile output |
| `--format html|json` | Documentation output format. Default: `html` |
| `--query <selectors>` | Comma-separated JSON selectors, such as `MyAPI` or `file:src/API.jo`; implies `--format json` |
| `--out <dir>` | Documentation output directory |
| `--title <name>` | Documentation title |
| `--readme <file>` | Markdown file to use as the generated documentation home page |
| `--include-private` | Include private symbols |
| `--include-source` | Embed source code in output |

`--format json` writes a JSON array to stdout. `--query` implies
`--format json`. JSON output does not use `--out`.
Without `--query`, JSON output contains the public API surface from the
positional source files. With `--query`, selectors are comma-separated and may
name symbols or source files with `file:<path>`.
Symbol selectors are resolved from the language default scope; for example `~`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is ~ symbol? It looks like some kind of special symbol. Is it actually a Jo operator ~? If so, it is a confusing example for the documentation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's a little confusing, I'll remove it. ~ is defined in standard library:

val x = 3 ~ "a"
val y = "a" ~ 4 ~ 6

val a ~ b = x
val c ~ h ~ w = y

is just an ordinary symbol query. A query may omit positional source files; in
that case it searches the loaded SAST libraries, including stdlib unless
`--no-stdlib` is set and any `--lib` paths.

Comment on lines +62 to +65

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit of a ranaway sentence. If I understood it correctly, here is how I would make it easier to read:

Suggested change
is just an ordinary symbol query. A query may omit positional source files; in
that case it searches the loaded SAST libraries, including stdlib unless
`--no-stdlib` is set and any `--lib` paths.
is just an ordinary symbol query. A query may omit positional source files; in
that case it searches any `--lib` paths and the loaded SAST libraries (including stdlib unless
`--no-stdlib` is set).

But correct me if I misunderstood it.

### App compilation

| Flag | Description |
Expand Down Expand Up @@ -104,6 +117,24 @@ jo compile --doc lib/Core.jo lib/List.jo \
--title "Jo Standard Library"
```

Emit machine-readable docs for a source file:

```sh
jo compile --doc --format json src/API.jo
```

Emit machine-readable docs for selected symbols:

```sh
jo compile --doc --query 'MyAPI,jo.py' src/API.jo
```

Query stdlib docs from SAST files only:

```sh
jo compile --doc --query jo.List
```

## Notes

`jo compile` is a low-level escape hatch for scripts, CI pipelines, or experiments that don't fit the standard project layout. For normal project workflows, prefer the higher-level commands such as `jo build`, `jo run <module>`, and [`jo doc`](doc.md).
Expand Down
18 changes: 18 additions & 0 deletions docs/usage/reference/compiler-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,26 @@ above plus these documentation-specific options.

| Option | Form | Description |
|--------|------|-------------|
| `--format <html|json>` | single value | Documentation output format. Default: `html`. |
| `--query <selectors>` | comma list | Select symbols or source files for JSON documentation output. Implies `--format json`. |
| `--out <dir>` | single value | Documentation output directory. Default: `docs`. |
| `--title <name>` | single value | Documentation title. Default: `API Documentation`. |
| `--readme <file>` | single value | Markdown file to use as the generated documentation home page. |
| `--include-private` | flag | Include private symbols. |
| `--include-source` | flag | Embed source code in the generated documentation. |

`--format json` writes a bare JSON array to stdout and rejects `--out`.
`--query` implies `--format json`.
Without `--query`, it emits the public API surface from the positional source
files. `--query` accepts comma-separated selectors:

| Selector | Meaning |
|----------|---------|
| `<symbol>` | Symbol resolved from the language default scope. Aggregate symbols are emitted structurally with their members. |
| `file:<path>` | Queryable symbols whose source file matches the path. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a use case in mind for the file:<path> selector? Can't the same be achieved by providing just the <path> as the compilation target?
I would err on the simplicity side adding things only if they have a use case.


When `--query` is present, positional source files are optional. With no source
files, the query searches the loaded SAST libraries: stdlib by default, runtime
API libraries selected by `--use-runtime-api`, and any `--lib` paths.

Kind-qualified selectors such as `def:<symbol>` are reserved for future use.
2 changes: 1 addition & 1 deletion stack-lang/cli/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ object Main:
| jo versions use <version> Switch the active compiler version
| jo versions remove <version> Remove an installed compiler version
| jo compile [options] <source.jo> Compile application or library
| jo compile --doc [options] <files...> Generate documentation from source files
| jo compile --doc [options] [files...] Generate documentation from source files or queried SAST libraries
| jo doc [module] Generate module documentation
| jo help Show this help message
|
Expand Down
73 changes: 62 additions & 11 deletions stack-lang/doc/Compiler.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package doc

import sast.*
import sast.Trees.FileUnit

import typing.Typer
import reporting.Reporter
Expand All @@ -17,30 +18,41 @@ object Compiler:
val readme: Config.StringSetting = Config.StringSetting("--readme", "", "markdown file to use as home page")
val includePrivate: Config.BooleanSetting = Config.BooleanSetting("--include-private", false, "include private symbols")
val includeSource: Config.BooleanSetting = Config.BooleanSetting("--include-source", false, "embed source code")
val format: Config.StringSetting = DocFormatSetting("--format", "html", "documentation output format: html or json")
val query: Config.StringSetting = Config.StringSetting("--query", "", "comma-separated documentation selectors for JSON output")

val docOptions: List[cli.OptionParser.Setting[?]] =
outputDir :: title :: readme :: includePrivate :: includeSource :: Config.commonOptions
outputDir :: title :: readme :: includePrivate :: includeSource :: format :: query :: Config.commonOptions

class DocFormatSetting(flag: String, default: String, desc: String)
extends Config.StringSetting(flag, default, desc):
override def validate()(using cf: Config, rp: Reporter): Unit =
val fmt = value
if fmt != "html" && fmt != "json" then
rp.error(s"Option $flag must be one of: html, json")

def main(args: Array[String]): Unit =
given Reporter = Reporter.createReporter()

val (config, sources) = cli.OptionParser.parseConfig(args, docOptions)

if sources.isEmpty then
given Config = config

if sources.isEmpty && query.value.trim.isEmpty then
println("Usage: jo doc <sources...> [options]")
println()
println("Options:")
println(" --out <dir> Output directory (default: docs)")
println(" --title <name> Project title for documentation")
println(" --format <html|json> Output format (default: html)")
println(" --query <selectors> JSON selectors, e.g. jo.py,file:src/API.jo")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does jo.py refer to some jo symbol , or to a python file?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jo.py is a symbolic selector, file:src/API.jo is a file selector.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using a symbolic selector that resembles a file with a classic extension together with a file selector is confusing, especially for people that are not aware that jo.py exists in the jo library.

println(" --include-private Include private symbols")
println(" --include-source Embed source code in output")
println()
println("Examples:")
println(" jo doc lib/Core.jo lib/List.jo --out site/api")
println(" jo doc src/main.jo --out docs --title MyProject")
return

given Config = config
println(" jo doc --query jo.List")
System.exit(1)

Reporter.monitor():
compile(sources)
Expand All @@ -49,16 +61,55 @@ object Compiler:
def compile(sources: List[String])(using rp: Reporter, config: Config): Unit =
val rootNameTable = new NameTable
given lazyDefn: Definitions.Lazy = Definitions.Lazy(rootNameTable)
val queryText = query.value.trim
val jsonOutput = format.value == "json" || queryText.nonEmpty

// Parse and type check
val (units, _) = sources |> Typer.parseStep |> Typer.typeStep
def writeJson(units: List[FileUnit], filter: DocQuery.Filter)(using Definitions): Unit =
val out = new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8))
DocQuery.emitJson(units, filter, includePrivate.value, out)
out.flush()

if rp.hasErrors then
println("Errors occurred during type checking. Documentation not generated.")
if jsonOutput && sources.isEmpty && Config.libPaths.value.isEmpty then
if outputDir.value != outputDir.default then
Reporter.error("--out is only supported with --format html")
return

DocQuery.reportNoMatches(queryText)
return

given Definitions = lazyDefn.value
// Parse and type check
val (units, delayedUnits) = sources |> Typer.parseStep |> Typer.typeStep

if rp.hasErrors then return

given defn: Definitions = lazyDefn.value

if jsonOutput then
if outputDir.value != outputDir.default then
Reporter.error("--out is only supported with --format html")
return

val filter = DocQuery.parse(queryText, defn.rootNameTable)
if rp.hasErrors then return

val libraryUnits =
if queryText.isEmpty then
Nil
else
delayedUnits.forceIf: unit =>
filter.symbols.exists: querySymbol =>
unit.owner.containedIn(querySymbol) || querySymbol.containedIn(unit.owner)

delayedUnits.force()

val filteredUnits = DocQuery.filterUnits(units, libraryUnits, filter)
if rp.hasErrors then return
writeJson(filteredUnits, filter)

else
generateHtmlDoc(units)

def generateHtmlDoc(units: List[FileUnit])(using Config, Definitions): Unit =
val outputPath = Paths.get(outputDir.value)
val includePrivateVal = includePrivate.value

Expand Down
Loading
Loading