Getting Started

Add the Dependency

In your build.mill (Mill build tool), add Ascribe as a dependency:

def mvnDeps = Seq(
  mvn"io.eleven19.ascribe::ascribe:0.2.1",
  mvn"io.eleven19.ascribe::ascribe-asg:0.2.1",
  mvn"io.eleven19.ascribe::ascribe-bridge:0.2.1",
  mvn"io.eleven19.ascribe::ascribe-pipeline:0.2.1"
)

Or with sbt:

libraryDependencies ++= Seq(
  "io.eleven19.ascribe" %% "ascribe"          % "0.2.1",
  "io.eleven19.ascribe" %% "ascribe-asg"      % "0.2.1",
  "io.eleven19.ascribe" %% "ascribe-bridge"   % "0.2.1",
  "io.eleven19.ascribe" %% "ascribe-pipeline" % "0.2.1"
)

Parse an AsciiDoc Document

The main entry point is Ascribe.parse, which takes a raw AsciiDoc string and returns a Parsley Result:

import io.eleven19.ascribe.Ascribe
import io.eleven19.ascribe.ast.Document

Ascribe.parse("= Title\n\nParagraph text.\n") match
  case parsley.Success(doc) => println(doc)
  case parsley.Failure(msg) => println(s"Parse error: $msg")

The returned Document is an AST (Abstract Syntax Tree) with full source position tracking.

Convert to ASG

The bridge module converts the parser's AST into the ASG (Abstract Semantic Graph), which matches the official AsciiDoc TCK schema:

import io.eleven19.ascribe.bridge.AstToAsg

val astDoc = Ascribe.parse("= Title\n\nParagraph text.\n").get
val asgDoc = AstToAsg.convert(astDoc)

Encode to JSON

Use AsgCodecs to serialize the ASG to JSON:

import io.eleven19.ascribe.asg.AsgCodecs

val json = AsgCodecs.encode(asgDoc)
println(json)

This produces JSON matching the AsciiDoc TCK expected format, with "name" as the type discriminator field.

Using the AST DSL

The ast.dsl module provides a concise builder syntax for constructing AST nodes without position boilerplate:

import io.eleven19.ascribe.ast.dsl.{*, given}
import scala.language.implicitConversions

val doc = document(
  heading(1, text("My Document")),
  paragraph("Hello ", bold("world"), "!"),
  unorderedList(
    listItem("First item"),
    listItem("Second item")
  )
)

String values are implicitly converted to Text nodes.

Using the ASG DSL

The asg.dsl module provides a similar builder for ASG nodes with implicit location threading:

import io.eleven19.ascribe.asg.dsl.{*, given}

val doc = document(
  paragraph(text("Hello"), span("strong", "constrained", text("world")))
)

Override the default location by providing your own given Location:

given Location = loc(1, 1, 3, 9)
val located = document(paragraph(text("Hello")))

Process Documents with the Pipeline

The pipeline module provides renderers, rewrite rules, and file I/O using Kyo effects:

import io.eleven19.ascribe.pipeline.*
import io.eleven19.ascribe.pipeline.dsl.*

// Parse, remove comments, and render back to AsciiDoc
val result = Pipeline
  .fromString("= Title\n\n// a comment\n\nHello.\n")
  .rewrite(removeComments)
  .runToString

For file-based processing:

val pipeline = Pipeline
  .from(FileSource.fromDirectory(inputDir))
  .rewrite(removeComments)

pipeline.runTo(FileSink.toDirectory(outputDir))