Skip to content

Commit 7bc49bb

Browse files
reidspencerclaude
andcommitted
Add bastify command to generate BAST files from RIDDL
Rename bast-gen to bastify with simplified interface: - Takes single .riddl file argument - Outputs .bast file next to input file - Removed unnecessary output-dir and output-file options Usage: riddlc bastify <input.riddl> Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 21698ec commit 7bc49bb

2 files changed

Lines changed: 34 additions & 55 deletions

File tree

commands/jvm/src/main/scala/com/ossuminc/riddl/commands/BastGenCommand.scala renamed to commands/jvm/src/main/scala/com/ossuminc/riddl/commands/BastifyCommand.scala

Lines changed: 30 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,58 +16,53 @@ import scopt.OParser
1616

1717
import java.nio.file.{Files, Path}
1818

19-
object BastGenCommand {
20-
val cmdName = "bast-gen"
19+
object BastifyCommand {
20+
val cmdName = "bastify"
2121

2222
case class Options(
2323
inputFile: Option[Path] = None,
24-
outputDir: Option[Path] = None,
25-
outputFile: Option[Path] = None,
2624
command: String = cmdName
2725
) extends PassCommandOptions {
26+
def outputDir: Option[Path] = inputFile.map(_.getParent).orElse(Some(Path.of(".")))
27+
2828
override def check: Messages = {
29-
val msgs1 = if inputFile.isEmpty then {
30-
Messages.errors("An input file was not provided.")
31-
} else Messages.empty
32-
// outputDir check is optional for bast-gen since we allow -o for direct file output
33-
msgs1
29+
if inputFile.isEmpty then
30+
Messages.errors("A .riddl input file is required.")
31+
else if !inputFile.get.toString.endsWith(".riddl") then
32+
Messages.errors("Input file must have .riddl extension.")
33+
else
34+
Messages.empty
3435
}
3536
}
3637
}
3738

3839
/** A command to generate BAST (Binary AST) files from RIDDL input.
40+
*
41+
* The output .bast file is placed next to the input .riddl file.
3942
*
4043
* Usage:
41-
* riddlc bast-gen <input.riddl> -o <output.bast>
42-
* riddlc bast-gen <input.riddl> --output-dir <dir>
44+
* riddlc bastify <input.riddl>
4345
*/
44-
class BastGenCommand(using pc: PlatformContext) extends PassCommand[BastGenCommand.Options](BastGenCommand.cmdName) {
45-
import BastGenCommand.Options
46+
class BastifyCommand(using pc: PlatformContext) extends PassCommand[BastifyCommand.Options](BastifyCommand.cmdName) {
47+
import BastifyCommand.Options
4648

4749
override def getOptionsParser: (OParser[Unit, Options], Options) = {
4850
import builder.*
49-
cmd(BastGenCommand.cmdName)
50-
.text("Generate a BAST (Binary AST) file from RIDDL input")
51+
cmd(BastifyCommand.cmdName)
52+
.text("Convert a RIDDL file to BAST (Binary AST) format")
5153
.children(
52-
inputFile((v, c) => c.copy(inputFile = Some(v.toPath))),
53-
opt[java.io.File]('o', "output")
54-
.optional()
55-
.action((v, c) => c.copy(outputFile = Some(v.toPath)))
56-
.text("The output BAST file path"),
57-
outputDir((v, c) => c.copy(outputDir = Some(v.toPath)))
54+
inputFile((v, c) => c.copy(inputFile = Some(v.toPath)))
5855
) -> Options()
5956
}
6057

6158
override def interpretConfig(config: Config): Options = {
6259
val obj = config.getObject(commandName).toConfig
6360
val inputFile = Path.of(obj.getString("input-file"))
64-
val outputDir = if obj.hasPath("output-dir") then Some(Path.of(obj.getString("output-dir"))) else None
65-
val outputFile = if obj.hasPath("output") then Some(Path.of(obj.getString("output"))) else None
66-
Options(Some(inputFile), outputDir, outputFile, commandName)
61+
Options(Some(inputFile), commandName)
6762
}
6863

6964
override def overrideOptions(options: Options, newOutputDir: Path): Options = {
70-
options.copy(outputDir = Some(newOutputDir))
65+
options // outputDir is derived from inputFile, so no override needed
7166
}
7267

7368
override def getPasses(options: Options): PassCreators = {
@@ -82,28 +77,22 @@ class BastGenCommand(using pc: PlatformContext) extends PassCommand[BastGenComma
8277
options: Options,
8378
outputDirOverride: Option[Path]
8479
): Either[Messages, PassesResult] = {
85-
// First run the passes to generate the BAST
8680
super.run(options, outputDirOverride) match {
8781
case Left(errors) => Left(errors)
8882
case Right(result) =>
89-
// Get the BAST output
9083
result.outputOf[BASTOutput](BASTWriterPass.name) match {
9184
case None =>
9285
Left(Messages.errors("BASTWriter did not produce output"))
9386
case Some(bastOutput) =>
94-
// Determine the output path
95-
val outputPath = determineOutputPath(options, outputDirOverride)
96-
97-
// Write the BAST file
87+
val outputPath = determineOutputPath(options)
9888
try {
99-
// Create parent directories if needed
10089
val parent = outputPath.getParent
10190
if parent != null && !Files.exists(parent) then
10291
Files.createDirectories(parent)
10392
end if
10493

10594
Files.write(outputPath, bastOutput.bytes)
106-
pc.log.info(s"Generated BAST file: $outputPath (${bastOutput.bytes.length} bytes, ${bastOutput.nodeCount} nodes)")
95+
pc.log.info(s"Generated: $outputPath (${bastOutput.bytes.length} bytes, ${bastOutput.nodeCount} nodes)")
10796
Right(result)
10897
} catch {
10998
case ex: Exception =>
@@ -113,25 +102,15 @@ class BastGenCommand(using pc: PlatformContext) extends PassCommand[BastGenComma
113102
}
114103
}
115104

116-
private def determineOutputPath(options: Options, outputDirOverride: Option[Path]): Path = {
117-
// Priority: explicit output file > output dir + default (next to input)
118-
options.outputFile match {
119-
case Some(path) => path
120-
case None =>
121-
val inputPath = options.inputFile.get.toAbsolutePath
122-
val inputName = inputPath.getFileName.toString
123-
val bastName = inputName.replaceAll("\\.(riddl|RIDDL)$", "") + ".bast"
124-
125-
// Default: place .bast file next to the source .riddl file
126-
val dir = outputDirOverride
127-
.orElse(options.outputDir)
128-
.getOrElse(inputPath.getParent match {
129-
case null => Path.of(".")
130-
case p => p
131-
})
132-
133-
dir.resolve(bastName)
105+
private def determineOutputPath(options: Options): Path = {
106+
val inputPath = options.inputFile.get.toAbsolutePath
107+
val inputName = inputPath.getFileName.toString
108+
val bastName = inputName.replaceAll("\\.riddl$", ".bast")
109+
val dir = inputPath.getParent match {
110+
case null => Path.of(".")
111+
case p => p
134112
}
113+
dir.resolve(bastName)
135114
}
136115

137116
override def loadOptionsFrom(configFile: Path): Either[Messages, Options] = {

commands/jvm/src/main/scala/com/ossuminc/riddl/commands/CommandLoader.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ object CommandLoader:
2323
def loadCommandNamed(name: String)(using io: PlatformContext): Either[Messages, Command[?]] =
2424
if io.options.verbose then io.log.info(s"Loading command: $name") else ()
2525
name match
26-
case "about" => Right(AboutCommand())
27-
case "bast-gen" => Right(BastGenCommand())
28-
case "dump" => Right(DumpCommand())
26+
case "about" => Right(AboutCommand())
27+
case "bastify" => Right(BastifyCommand())
28+
case "dump" => Right(DumpCommand())
2929
case "flatten" => Right(FlattenCommand())
3030
case "from" => Right(FromCommand())
3131
case "hugo" => Right(HugoCommand())
@@ -45,7 +45,7 @@ object CommandLoader:
4545
def commandOptionsParser(using io: PlatformContext): OParser[Unit, ?] = {
4646
val optionParsers = Seq(
4747
AboutCommand().getOptionsParser._1.asInstanceOf[OParser[Unit, CommandOptions]],
48-
BastGenCommand().getOptionsParser._1.asInstanceOf[OParser[Unit, CommandOptions]],
48+
BastifyCommand().getOptionsParser._1.asInstanceOf[OParser[Unit, CommandOptions]],
4949
DumpCommand().getOptionsParser._1.asInstanceOf[OParser[Unit, CommandOptions]],
5050
FlattenCommand().getOptionsParser._1.asInstanceOf[OParser[Unit, CommandOptions]],
5151
FromCommand().getOptionsParser._1.asInstanceOf[OParser[Unit, CommandOptions]],

0 commit comments

Comments
 (0)