Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ docs/mdoc-invocation.txt
local-bindgen
bin
worktree-main
/.idea/
5 changes: 5 additions & 0 deletions modules/bindgen/src/main/scala/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ case class Config(
linkName: Option[LinkName],
indentSize: IndentationSize,
indents: Indentation,
useBraces: UseBraces,
cImports: List[CImport],
clangFlags: List[ClangFlag],
quiet: Quiet,
Expand Down Expand Up @@ -56,6 +57,7 @@ object Config:
linkName = None,
indentSize = defaults.indentSize,
indents = defaults.indents,
useBraces = UseBraces.No,
cImports = Nil,
clangFlags = Nil,
quiet = Quiet.No,
Expand Down Expand Up @@ -99,6 +101,9 @@ object Quiet extends YesNo[Quiet]
opaque type MultiFile = Boolean
object MultiFile extends YesNo[MultiFile]

opaque type UseBraces = Boolean
object UseBraces extends YesNo[UseBraces]

enum Lang:
case Scala, C

Expand Down
11 changes: 11 additions & 0 deletions modules/bindgen/src/main/scala/cli/arguments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ object CLI:
.withDefault(2)
.map(IndentationSize.apply(_))

private val bracesNotIndents =
Opts
.flag(
"use-braces",
help =
"Generate Scala part of the binding with braces or significant whitespace"
)
.orFalse
.map(UseBraces.apply(_))

private val isScala =
Opts.flag("scala", help = "Generate Scala part of the binding").orFalse

Expand Down Expand Up @@ -378,6 +388,7 @@ object CLI:
linkName,
indentationSize,
indentation,
bracesNotIndents,
cImport,
(clangInclude, clangFlag).mapN(_ ++ _),
quiet,
Expand Down
7 changes: 3 additions & 4 deletions modules/bindgen/src/main/scala/render/alias.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def alias(model: Def.Alias, line: Appender)(using
AliasResolver,
Config
): Exported =

val (underlyingType, enableConstructor) =
model.underlying match
case Pointer(Reference(Name.Unnamed)) =>
Expand All @@ -31,8 +32,7 @@ def alias(model: Def.Alias, line: Appender)(using

renderComment(line, model.meta)
line(s"${modifier}type ${model.name} = ${scalaType(underlyingType)}")
line(s"object ${sanitiseBeforeColon(model.name)}: ")
nest {
objectBlock(line)(s"object ${sanitiseBeforeColon(model.name)}") {
model.underlying match
case Reference(Name.BuiltIn(name)) =>
line(s"val _tag: Tag[${model.name}] = summon[Tag[${name.full}]]")
Expand All @@ -50,8 +50,7 @@ def alias(model: Def.Alias, line: Appender)(using
line(
s"inline def apply(inline o: ${scalaType(underlyingType)}): ${model.name} = o"
)
line(s"extension (v: ${model.name})")
nest {
defBlock(line)(s"extension (v: ${model.name})") {
line(s"inline def value: ${scalaType(underlyingType)} = v")
if isFunctionPointer then
line(s"inline def toPtr: $voidPtr = CFuncPtr.toPtr(v)")
Expand Down
119 changes: 60 additions & 59 deletions modules/bindgen/src/main/scala/render/binding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,14 @@ def renderBinding(

if !multiFileMode && hasAnyTypes then
val l = to(simpleStream("types"))
l("object types:")
nest {
val l = to(simpleStream("types"))
if hasStructs then l(s"export _root_.${packageName}.structs.*")
if hasAliases then l(s"export _root_.${packageName}.aliases.*")
if hasUnions then l(s"export _root_.${packageName}.unions.*")
if hasAnyEnums then l(s"export _root_.${packageName}.enumerations.*")
objectBlock(l)("object types") {
nest {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this extra nesting is unnecessary (no need to fix, I will address myself after merging this)

val l = to(simpleStream("types"))
if hasStructs then l(s"export _root_.${packageName}.structs.*")
if hasAliases then l(s"export _root_.${packageName}.aliases.*")
if hasUnions then l(s"export _root_.${packageName}.unions.*")
if hasAnyEnums then l(s"export _root_.${packageName}.enumerations.*")
}
}
end if

Expand All @@ -245,16 +246,32 @@ private def commentException(element: Any, exc: Throwable) =
throw exc
end commentException

//wraps a block in the supplied object/class/trait if the render mode is such
private def maybeObjectBlock(out: LineBuilder, mode: RenderMode)(
objectHeader: String
)(
f: Config ?=> Unit
)(using Config) =
lazy val openDelimiter: String =
if summon[Config].useBraces.value then " {" else ":"
lazy val closeDelimiter: Option[String] =
if summon[Config].useBraces.value then Some("}") else None

if mode == RenderMode.Objects then to(out)(s"$objectHeader$openDelimiter")
nestIf(mode == RenderMode.Objects) {
f
}
if mode == RenderMode.Objects then closeDelimiter.foreach(to(out))
end maybeObjectBlock

private def renderAliases(
aliases: List[Def.Alias],
out: LineBuilder,
mode: RenderMode,
typeImports: TypeImports
)(using Config, AliasResolver, Context) =
val exported = List.newBuilder[Exported]
if mode == RenderMode.Objects then out.appendLine("object aliases:")

nestIf(mode == RenderMode.Objects) {
maybeObjectBlock(out, mode)("object aliases") {
if mode == RenderMode.Objects then typeImports.render(out)
exported ++= renderAll(aliases, out, alias)
}
Expand All @@ -270,8 +287,7 @@ private def renderExports(
case RenderMode.Objects =>
if exports.nonEmpty then
out.emptyLine
to(out)("object all:")
nest {
maybeObjectBlock(out, mode)("object all") {
exports.distinct.foreach { (scope, name) =>
to(out)(s"export _root_.$packageName.$scope.$name")
}
Expand All @@ -290,10 +306,7 @@ private def renderUnions(
typeImports: TypeImports
)(using Config, AliasResolver, Context) =
val exported = List.newBuilder[Exported]

if mode == RenderMode.Objects then out.appendLine("object unions:")

nestIf(mode == RenderMode.Objects) {
maybeObjectBlock(out, mode)("object unions") {
if mode == RenderMode.Objects then typeImports.render(out)
exported ++= renderAll(unions, out, union)
}
Expand All @@ -307,9 +320,7 @@ private def renderStructs(
typeImports: TypeImports
)(using Config, AliasResolver, Context) =
val exported = List.newBuilder[Exported]
if mode == RenderMode.Objects then out.appendLine("object structs:")

nestIf(mode == RenderMode.Objects) {
maybeObjectBlock(out, mode)("object structs") {
if mode == RenderMode.Objects then typeImports.render(out)
exported ++= renderAll(structs, out, struct)
}
Expand All @@ -322,8 +333,7 @@ private def renderConstants(
mode: RenderMode
)(using Config, AliasResolver) =
if enums.nonEmpty then
if mode == RenderMode.Objects then to(out)("object constants:")
nestIf(mode == RenderMode.Objects) {
maybeObjectBlock(out, mode)("object constants") {
constants(Constants(enums), to(out))
}

Expand Down Expand Up @@ -392,17 +402,18 @@ private def enumPredef(

val traitName = enumBaseTraitName(intType)

lb.appendLine(
s"private[${safePackageName}] trait $traitName[T](using eq: T =:= $renderedScalaType):"
)
lb.appendLine(s" given Tag[T] = Tag.$renderedTagName.asInstanceOf[Tag[T]]")
lb.appendLine(s" extension (inline t: T)")
lb.appendLine(s" inline def value: $renderedScalaType = eq.apply(t)")
if intType.base == IntegralBase.Int then
lb.appendLine(s" inline def int: CInt = eq.apply(t).toInt")
if intType.sign == SignType.Unsigned then
lb.appendLine(s" inline def uint: CUnsignedInt = eq.apply(t)")

objectBlock(to(lb))(
s"private[${safePackageName}] trait $traitName[T](using eq: T =:= $renderedScalaType)"
) {
to(lb)(s"given Tag[T] = Tag.$renderedTagName.asInstanceOf[Tag[T]]")
defBlock(to(lb))("extension (inline t: T)") {
to(lb)(s"inline def value: $renderedScalaType = eq.apply(t)")
if intType.base == IntegralBase.Int then
to(lb)(s"inline def int: CInt = eq.apply(t).toInt")
if intType.sign == SignType.Unsigned then
to(lb)(s"inline def uint: CUnsignedInt = eq.apply(t)")
}
}
lb.result.linesIterator.toList
end enumPredef

Expand All @@ -420,15 +431,13 @@ private def renderEnumerations(
val exported = List.newBuilder[Exported]

if enumBases.nonEmpty then
if mode == RenderMode.Objects then out.appendLine("object predef:")
nestIf(mode == RenderMode.Objects) {
maybeObjectBlock(out, mode)("object predef") {
val safePackageName = packageName.split('.').last
enumBases.foreach: base =>
enumPredef(safePackageName, base).foreach(to(out))
}
if mode == RenderMode.Objects then
out.appendLine("\n\nobject enumerations:")
nestIf(mode == RenderMode.Objects) {
if mode == RenderMode.Objects then out.emptyLine
maybeObjectBlock(out, mode)("object enumerations") {
if mode == RenderMode.Objects then to(out)("import predef.*")
exported ++= renderAll(
enums,
Expand Down Expand Up @@ -480,20 +489,14 @@ private def renderScalaFunctions(
if exportMode == ExportMode.No then

if hasExternFunctions then
if renderMode == RenderMode.Objects then
summon[Config].linkName.foreach { l =>
out.append(s"""@link("$l")""")
val objectHeader = summon[Config].linkName
.map { l =>
s"""@link("$l")\n"""
}
end if

nestIf(renderMode == RenderMode.Objects) {
if renderMode == RenderMode.Objects then
out.appendLine(
s"\n@extern\nprivate[$safePackageName] object extern_functions:"
)
typeImports.render(out)
else out.appendLine("\n")

.getOrElse("") +
s"\n@extern\nprivate[$safePackageName] object extern_functions"
maybeObjectBlock(out, renderMode)(objectHeader) {
if renderMode == RenderMode.Objects then typeImports.render(out)
exported ++= renderAll(
scalaExternFunctions.toList.sortBy(functionSorter),
out,
Expand All @@ -503,9 +506,7 @@ private def renderScalaFunctions(
end if

if hasRegularFunctions || hasExternFunctions then
if renderMode == RenderMode.Objects then
out.appendLine(s"\nobject functions:")
nestIf(renderMode == RenderMode.Objects) {
maybeObjectBlock(out, renderMode)(s"\nobject functions") {
if renderMode == RenderMode.Objects then typeImports.render(out)

if hasExternFunctions && renderMode == RenderMode.Objects then
Expand Down Expand Up @@ -539,8 +540,9 @@ private def renderScalaFunctions(
!isInit
}

out.appendLine("trait ExportedFunctions:")
nest {
val line = to(out)

objectBlock(line)("trait ExportedFunctions") {
if renderMode == RenderMode.Objects then typeImports.render(out)
renderAll(
modified(ExportLocation.Trait),
Expand All @@ -549,9 +551,9 @@ private def renderScalaFunctions(
)
}

if renderMode == RenderMode.Objects then
out.appendLine(s"\nobject functions extends ExportedFunctions:")
nestIf(renderMode == RenderMode.Objects) {
maybeObjectBlock(out, renderMode)(
s"\nobject functions extends ExportedFunctions"
) {
if renderMode == RenderMode.Objects then typeImports.render(out)
renderAll(
modified(
Expand All @@ -561,7 +563,6 @@ private def renderScalaFunctions(
renderFunction(_, _, renderMode)
)
}

end if
end if
exported.result()
Expand Down
41 changes: 19 additions & 22 deletions modules/bindgen/src/main/scala/render/enumeration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ def enumeration(model: Def.Enum, line: Appender)(using

renderComment(line, model.meta)
line(s"opaque type $opaqueType = $underlyingTypeRender")
line(s"object $opaqueType extends $traitName[$opaqueType]:")

nest {
objectBlock(line)(s"object $opaqueType extends $traitName[$opaqueType]") {
line(s"given _tag: Tag[$opaqueType] = ${scalaTag(numericType)}")
if numericType.sign == SignType.Signed then
line(
Expand All @@ -38,32 +36,31 @@ def enumeration(model: Def.Enum, line: Appender)(using

line(lhs + " = " + rhs)
}
line(s"inline def getName(inline value: $opaqueType): Option[String] =")
nest {
line("inline value match")
nest {
defBlock(line)(
s"inline def getName(inline value: $opaqueType): Option[String] ="
) {
defBlock(line)("inline value match") {
model.values.foreach { case (constName, value) =>
line(s"""case ${escape(constName)} => Some("${escape(constName)}")""")
}
line("case _ => _root_.scala.None")
}
}
line(s"extension (a: $opaqueType)")
def wrap(exp: String) =
numericType match
case CType.NumericIntegral(
bindgen.IntegralBase.Char,
SignType.Signed
) =>
s"(($exp) & 0xff).toByte"
case CType.NumericIntegral(
bindgen.IntegralBase.Char,
SignType.Unsigned
) =>
s"(($exp) & 0xff.toUInt).toUByte"
case _ => exp
defBlock(line)(s"extension (a: $opaqueType)") {
def wrap(exp: String) =
numericType match
case CType.NumericIntegral(
bindgen.IntegralBase.Char,
SignType.Signed
) =>
s"(($exp) & 0xff).toByte"
case CType.NumericIntegral(
bindgen.IntegralBase.Char,
SignType.Unsigned
) =>
s"(($exp) & 0xff.toUInt).toUByte"
case _ => exp

nest {
line(s"inline def &(b: $opaqueType): $opaqueType = ${wrap("a & b")}")
line(s"inline def |(b: $opaqueType): $opaqueType = ${wrap("a | b")}")
line(s"inline def is(b: $opaqueType): Boolean = (a & b) == b")
Expand Down
7 changes: 3 additions & 4 deletions modules/bindgen/src/main/scala/render/function.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,9 @@ def renderFunction(
a @ Allocations(indices, returnAsWell)
) =>
val hasZone = if a.hasAny then "(using Zone)" else ""
line(
s"def ${f.name}$arglist$hasZone: ${scalaType(f.returnType)} = "
)
nest {
defBlock(line)(
s"def ${f.name}$arglist$hasZone: ${scalaType(f.returnType)} ="
) {
import scala.collection.mutable.Map as MutableMap

val allocationSizes: CType MutableMap Int =
Expand Down
Loading