diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml new file mode 100644 index 00000000..0b07b9b9 --- /dev/null +++ b/.github/workflows/publish-docs.yml @@ -0,0 +1,35 @@ +name: Publish Docs +on: + workflow_dispatch + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: coursier/cache-action@v6 + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 17 + - name: Build docs + run: ./mill core.docJar + - name: Upload GitHub Pages artifact + uses: actions/upload-pages-artifact@v1.0.4 + with: + path: out/core/docJar.dest/javadoc + deploy: + needs: build + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1.2.2 + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..bf39fbc9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,176 @@ +## Project overview + +The code in this repository is primarily divided into four top-level directories: +* `core/`: The base API and most of the implementation of Slinc +* `j17/`: Java 17 specific parts of the Slinc implementation +* `j19/`: Java 19 specific parts of the Slinc implementation +* `runtime/`: The complete Slinc project, with Java 17 and 19 implementations combined and the correct implementation chosen at runtime by your system. + +## Developing Slinc +Slinc is a somewhat involved project to program for. With implementations targeting Java 17 and Java 19, one needs access to both JDKs to develop and run the j17 and j19 projects. + +### Sdkman + +In order to develop Slinc, it's suggested that one goes to [sdkman.io](https://sdkman.io/) and installs their software development kit manager based on the platform you're working in. The sdkman website has easy to follow instructions for installing on MacOS and Linux, and the tool will allow you to install multiple JDKs and switch between them with ease. + +Once you have sdkman installed, you can use the following commands to install and use the standard JDKs for this project: + +#### Java 19 +* Install: `sdk i java 19-tem` +* Use: `sdk u java 19-tem` +* Default: `sdk d java 19-tem` + +#### Java 17 +* Install: `sdk i java 17.0.4.1-tem` +* Use: `sdk u java 17.0.4.1-tem` +* Default: `sdk d java 17.0.4.1-tem` + +`Install` will of course install the JVM in question to your system (specifically in a portion of your user directory). Once installed, sdkman will prompt you if you want to make the JDK that was installed your default JDK. Choosing to do so will make that JDK the one seen by most all programs launched by the user in question. + +`Use` will set the JDK visible for a certain terminal instance. This is useful if you want to build the entire project. Use will only effect the JDK choice for the terminal it's invoked in, and will not affect the user default JVM. + +`Default` will set the JDK that's visible for all programs launched after the command has been invoked. This is useful for reloading your code editor to work on a different java implementation for Slinc. + +### Editor +When developing Slinc, it's suggested to use [VSCode](https://code.visualstudio.com/) along with the [Metals](https://marketplace.visualstudio.com/items?itemName=scalameta.metals) extension. Slinc is heavily dependent on compile-time programming, and VSCode+Metals works very well with this development model. One can use other editors, but it's probably mandatory to use Metals. + +Using metals, one can import the build definition from mill. If one encounters an issue with the import failing for no discernable reason, try deleting the `out` directory and trying again. There is a problem with this project and mill failing to generate bloop configurations. If one encounters errors when viewing a code base that do not resolve themselves, it's suggested to try closing VSCode, killing all Java processes, and deleting .metals, .bloop, and out. Generally, this will fix all issues. + +When developing for Slinc, choose an implementation to focus on, and choose the appropriate JDK for it. Switch with the appropriate `default` command on sdkman, kill all java processes, and afterwards open the project with VSCode. The corresponding `j` project should be having no missing definition errors after this process. Switching between JDK versions follows the same process. + +## Compiling + +The following commands compile the Slinc projecs: + +* core: `./mill core.compile` +* j17: `./mill j17.compile` +* j19: `./mill j19.compile` +* runtime: `./mill runtime.compile` + +Compiling the entire project would normally be done by running `./mill _.compile`, but considering the different project have different JDK requirements, the full compilation takes the form of + +```bash +sdk u java 17.0.4.1-tem && \ +./mill core.compile && \ +./mill core.test.compile && \ +./mill j17.compile && \ +sdk u java 19-tem && \ +./mill j19.compile && \ +#optional sdk u java 17.0.4.1-tem && +./mill runtime.compile +``` + +Only j17 and j19 have a hard dependency on specific JDK major versions. Core and runtime can be compiled with either java 17 or 19 as you wish. + + +## Testing +Tests exist for all portions of the project. They can be executed by running `./mill .test`. Examples are: + +* `./mill j17.test` +* `./mill j19.test` +* `./mill core.test` +* `./mill runtime.test` + +Please note that testing runtime involves doing the delicate compilation dance listed above. + +Testing code is generally stored in the `core` project under `core/test/src`. Java 17, Java 19, and runtime specific tests may exist in the future, but at the moment, all implementations use a generic testing base. + +Tests in Slinc use munit and scalacheck. One can read how to use munit with scalacheck [here](https://scalameta.org/munit/docs/integrations/scalacheck.html) and how to use scalacheck [here](https://github.com/typelevel/scalacheck/blob/main/doc/UserGuide.md). + + +In order to develop a new test suite for Slinc, add the implementation to `core/test/src`. If the test suite is testing an implementation in `core` then one can define it in the normal way specified by the munit documentation. However, if it's meant to be a test of Slinc implementations, it should be defined in a generic fashion like so: + +```scala +package fr.hammons.slinc + +import munit.ScalaCheckSuite + +trait MyTestSuite(slinc: Slinc) extends ScalaCheckSuite: + import slinc.{*,given} + test("myTest") { + assertEquals(4,4) + } +``` + +After defining this in core, add the test to `j17/test/src/fr/hammons/slinc` and `j19/test/src/fr/hammons/slinc` with the following code: + +```scala +package fr.hammons.slinc + +class MyTestSuite17 extends MyTestSuite(Slinc17.default) +``` + +If one's test suite concerns JIT compilation, one can use `noJit` and `immediate` implementations to make one's test suites test the unjitted and jitted versions of the runtime. + +### Troubleshooting tests + +Sometimes when running a freshly written test, or testing freshly written code, one might encounter a situation where the test suite will stop testing early, or never stop running. + +Generally, the test suite will stop running early when some part of the Slinc runtime fails to initialize properly. One can easily detect if this is the case by moving some test code out of the test section into the root of the suite. + +Observe the following example: + +```scala +trait MySuite(s: Slinc) extends ScalacheckSuite: + import s.{*, given} + test("myTest") { + assertEquals(sizeOf[Int], 4.as[SizeT] + } +``` + +should be rewritten to + +```scala +trait MySuite(s: Slinc) extends ScalacheckSuite: + import s.{*,given} + sizeOf[Int] + 4.as[SizeT] + + test("myTest") { + assertEquals(sizeOf[Int], 4.as[SizeT]) + } +``` + +This tends to force the test suite to actually reveal the exception that's breaking it, and will help one fix the issue in question. + +When a test suite continues forever, the cause is usually the same, but with regards to a propertyBased test: + +```scala +trait MySuite(s: Slinc) extends ScalacheckSuite: + import s.{*, given} + + property("myProperty") { + forAll{ + (i: Int) => + Scope.confined{ + val ptr = Ptr.blank[CInt] + + !ptr = i + assertEquals(!ptr, i) + } + } + } +``` + +should be changed to + +```scala +trait MySuite(s: Slinc) extends ScalacheckSuite: + import s.{*, given} + val ptr = Ptr.blank[CInt] + + !ptr = 4 + !ptr + + property("myProperty") { + forAll{ + (i: Int) => + Scope.confined{ + val ptr = Ptr.blank[CInt] + + !ptr = i + assertEquals(!ptr, i) + } + } + } +``` \ No newline at end of file diff --git a/README.md b/README.md index 48b4a727..354c27bb 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,58 @@ -[![pipeline status](https://gitlab.com/mhammons/slinc/badges/master/pipeline.svg)](https://gitlab.com/mhammons/slinc/-/commits/master)![Maven Central](https://img.shields.io/maven-central/v/io.gitlab.mhammons/slinc_3)[![fork me](https://img.shields.io/badge/gitlab-fork%20me-orange?logo=gitlab)](https://gitlab.com/mhammons/slinc) +![workflow status](https://github.com/markehammons/slinc/actions/workflows/ci.yml/badge.svg)![Maven Central](https://img.shields.io/maven-central/v/io.gitlab.mhammons/slinc_3) + ``` S Lin C Scala Link to C ``` +Slinc is a Scala 3 library that allows users to interoperate with C code via Java 17+'s foreign api. + +It's designed to make use of Scala's type system, macros, and runtime multi-stage programming to make bindings to C from Scala. + +## Quickstart + +Slinc is published to Maven Central for Scala 3. It is built to take advantage of the cutting edge of Scala functionality and features, so your project will most certainly need to track the latest Scala version if you want to use the latest version of Slinc. Currently, the Scala version in use is `3.2.1`. + +### SBT setup + +In your `build.sbt`: + +```scala +libraryDependencies += "fr.hammons" %% "slinc-runtime" % "0.1.1-66-a2fa26-DIRTYd1a2c450" +//if forking and on Java 17 +javaOptions ++= Seq("--add-modules=jdk.incubator.foreign", "--enable-native-access=ALL-UNNAMED") +``` + +in .jvmopts in the root of your build: +``` +--add-modules=jdk.incubator.foreign +--enable-native-access=ALL-UNNAMED +``` + +For additional setup instructions, please refer to the configuration page. + +Once you have your build system set up, you can create a new file and write the following code: + +```scala +import fr.hammons.slinc.runtime.{*,given} + +case class div_t(quot: Int, rem: Int) derives Struct + +object MyLib derives Library: + def div(numer: Int, denom: Int): div_t = Library.binding + +@main def calc = + val (quot, rem) = Tuple.fromProduct(MyLib.div(5,2)) + println(s"Got a quotient of $quot and a remainder of $rem") +``` + +This library relies on the user importing the runtime from `fr.hammons.slinc.runtime`. + + + +### JVM support + + Please note that the main location for this repository is [gitlab](https://gitlab.com/mhammons/slinc). Please look there for the issue tracker and other things. The documentation website for Slinc can be found [here](https://mhammons.gitlab.io/slinc) \ No newline at end of file diff --git a/build.sc b/build.sc index 1a055e16..bfa9a093 100644 --- a/build.sc +++ b/build.sc @@ -57,6 +57,18 @@ object core with FacadeGenerationModule with BenchmarksModule { + def javacOptions = + super.javacOptions() ++ Seq("--release", "17") + + override def scalaDocOptions = T { + super.scalaDocOptions() ++ Seq( + "-project-logo", + (millSourcePath / "docs" / "_assets" / "images" / "logo.svg").toString, + "-project", "slinc" + ) + } + + def pomSettings = pomTemplate("slinc-core") def specializationArity = 4 @@ -116,8 +128,8 @@ object j19 extends BaseModule with PublishableModule with BenchmarksModule { def moduleDeps = Seq(core) def pomSettings = pomTemplate("slinc-java-19") - def javacOptions = - super.javacOptions() ++ Seq("--release", "19", "--enable-preview") + // def javacOptions = + // super.javacOptions() ++ Seq("--release", "19", "--enable-preview") object test extends BaseTest { def moduleDeps = super.moduleDeps ++ Seq(core.test) @@ -144,26 +156,28 @@ object j19 extends BaseModule with PublishableModule with BenchmarksModule { } } -// object `runtime-test` extends BaseModule with PublishableModule { - -// def pomSettings = pomTemplate("slinc-full") - -// override def ivyDeps = Agg( -// ivy"fr.hammons::slinc-j17:${publishVersion()}", -// ivy"fr.hammons::slinc-j19:${publishVersion()}" -// ) - -// object test extends Tests with TestModule.Munit { -// def ivyDeps = Agg(ivy"org.scalameta::munit:$munitVersion") - -// def jvm = T.input{ System.getProperty("java.version")} -// def moduleDeps = super.moduleDeps ++ Seq(core.test) -// def forkArgs = super.forkArgs() ++ Seq( -// "--enable-preview", -// "--enable-native-access=ALL-UNNAMED" -// ) ++ (if (jvm().startsWith("17")) { -// println("adding") -// Seq("--add-modules=jdk.incubator.foreign") -// } else Seq.empty) -// } -// } +object `runtime` extends BaseModule with PublishableModule { + + def pomSettings = pomTemplate("slinc-full") + + override def moduleDeps = Seq(j17, j19) + + // override def ivyDeps = Agg( + // ivy"fr.hammons::slinc-j17:${publishVersion()}", + // ivy"fr.hammons::slinc-j19:${publishVersion()}" + // ) + + object test extends Tests with TestModule.Munit { + def ivyDeps = Agg(ivy"org.scalameta::munit:$munitVersion") + + def jvm = T.input{ System.getProperty("java.version")} + def moduleDeps = super.moduleDeps ++ Seq(core.test) + def forkArgs = super.forkArgs() ++ Seq( + "--enable-preview", + "--enable-native-access=ALL-UNNAMED" + ) ++ (if (jvm().startsWith("17")) { + println("adding") + Seq("--add-modules=jdk.incubator.foreign") + } else Seq.empty) + } +} diff --git a/core/docs/_assets/images/logo.svg b/core/docs/_assets/images/logo.svg new file mode 100644 index 00000000..621ad099 --- /dev/null +++ b/core/docs/_assets/images/logo.svg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8e48ddf1b45069472d997800b4fce46b92fbafdb166f3bd0a701d73ae87dde6 +size 19393 diff --git a/core/docs/_docs/contributing/contributing.md b/core/docs/_docs/contributing/contributing.md new file mode 100644 index 00000000..bf39fbc9 --- /dev/null +++ b/core/docs/_docs/contributing/contributing.md @@ -0,0 +1,176 @@ +## Project overview + +The code in this repository is primarily divided into four top-level directories: +* `core/`: The base API and most of the implementation of Slinc +* `j17/`: Java 17 specific parts of the Slinc implementation +* `j19/`: Java 19 specific parts of the Slinc implementation +* `runtime/`: The complete Slinc project, with Java 17 and 19 implementations combined and the correct implementation chosen at runtime by your system. + +## Developing Slinc +Slinc is a somewhat involved project to program for. With implementations targeting Java 17 and Java 19, one needs access to both JDKs to develop and run the j17 and j19 projects. + +### Sdkman + +In order to develop Slinc, it's suggested that one goes to [sdkman.io](https://sdkman.io/) and installs their software development kit manager based on the platform you're working in. The sdkman website has easy to follow instructions for installing on MacOS and Linux, and the tool will allow you to install multiple JDKs and switch between them with ease. + +Once you have sdkman installed, you can use the following commands to install and use the standard JDKs for this project: + +#### Java 19 +* Install: `sdk i java 19-tem` +* Use: `sdk u java 19-tem` +* Default: `sdk d java 19-tem` + +#### Java 17 +* Install: `sdk i java 17.0.4.1-tem` +* Use: `sdk u java 17.0.4.1-tem` +* Default: `sdk d java 17.0.4.1-tem` + +`Install` will of course install the JVM in question to your system (specifically in a portion of your user directory). Once installed, sdkman will prompt you if you want to make the JDK that was installed your default JDK. Choosing to do so will make that JDK the one seen by most all programs launched by the user in question. + +`Use` will set the JDK visible for a certain terminal instance. This is useful if you want to build the entire project. Use will only effect the JDK choice for the terminal it's invoked in, and will not affect the user default JVM. + +`Default` will set the JDK that's visible for all programs launched after the command has been invoked. This is useful for reloading your code editor to work on a different java implementation for Slinc. + +### Editor +When developing Slinc, it's suggested to use [VSCode](https://code.visualstudio.com/) along with the [Metals](https://marketplace.visualstudio.com/items?itemName=scalameta.metals) extension. Slinc is heavily dependent on compile-time programming, and VSCode+Metals works very well with this development model. One can use other editors, but it's probably mandatory to use Metals. + +Using metals, one can import the build definition from mill. If one encounters an issue with the import failing for no discernable reason, try deleting the `out` directory and trying again. There is a problem with this project and mill failing to generate bloop configurations. If one encounters errors when viewing a code base that do not resolve themselves, it's suggested to try closing VSCode, killing all Java processes, and deleting .metals, .bloop, and out. Generally, this will fix all issues. + +When developing for Slinc, choose an implementation to focus on, and choose the appropriate JDK for it. Switch with the appropriate `default` command on sdkman, kill all java processes, and afterwards open the project with VSCode. The corresponding `j` project should be having no missing definition errors after this process. Switching between JDK versions follows the same process. + +## Compiling + +The following commands compile the Slinc projecs: + +* core: `./mill core.compile` +* j17: `./mill j17.compile` +* j19: `./mill j19.compile` +* runtime: `./mill runtime.compile` + +Compiling the entire project would normally be done by running `./mill _.compile`, but considering the different project have different JDK requirements, the full compilation takes the form of + +```bash +sdk u java 17.0.4.1-tem && \ +./mill core.compile && \ +./mill core.test.compile && \ +./mill j17.compile && \ +sdk u java 19-tem && \ +./mill j19.compile && \ +#optional sdk u java 17.0.4.1-tem && +./mill runtime.compile +``` + +Only j17 and j19 have a hard dependency on specific JDK major versions. Core and runtime can be compiled with either java 17 or 19 as you wish. + + +## Testing +Tests exist for all portions of the project. They can be executed by running `./mill .test`. Examples are: + +* `./mill j17.test` +* `./mill j19.test` +* `./mill core.test` +* `./mill runtime.test` + +Please note that testing runtime involves doing the delicate compilation dance listed above. + +Testing code is generally stored in the `core` project under `core/test/src`. Java 17, Java 19, and runtime specific tests may exist in the future, but at the moment, all implementations use a generic testing base. + +Tests in Slinc use munit and scalacheck. One can read how to use munit with scalacheck [here](https://scalameta.org/munit/docs/integrations/scalacheck.html) and how to use scalacheck [here](https://github.com/typelevel/scalacheck/blob/main/doc/UserGuide.md). + + +In order to develop a new test suite for Slinc, add the implementation to `core/test/src`. If the test suite is testing an implementation in `core` then one can define it in the normal way specified by the munit documentation. However, if it's meant to be a test of Slinc implementations, it should be defined in a generic fashion like so: + +```scala +package fr.hammons.slinc + +import munit.ScalaCheckSuite + +trait MyTestSuite(slinc: Slinc) extends ScalaCheckSuite: + import slinc.{*,given} + test("myTest") { + assertEquals(4,4) + } +``` + +After defining this in core, add the test to `j17/test/src/fr/hammons/slinc` and `j19/test/src/fr/hammons/slinc` with the following code: + +```scala +package fr.hammons.slinc + +class MyTestSuite17 extends MyTestSuite(Slinc17.default) +``` + +If one's test suite concerns JIT compilation, one can use `noJit` and `immediate` implementations to make one's test suites test the unjitted and jitted versions of the runtime. + +### Troubleshooting tests + +Sometimes when running a freshly written test, or testing freshly written code, one might encounter a situation where the test suite will stop testing early, or never stop running. + +Generally, the test suite will stop running early when some part of the Slinc runtime fails to initialize properly. One can easily detect if this is the case by moving some test code out of the test section into the root of the suite. + +Observe the following example: + +```scala +trait MySuite(s: Slinc) extends ScalacheckSuite: + import s.{*, given} + test("myTest") { + assertEquals(sizeOf[Int], 4.as[SizeT] + } +``` + +should be rewritten to + +```scala +trait MySuite(s: Slinc) extends ScalacheckSuite: + import s.{*,given} + sizeOf[Int] + 4.as[SizeT] + + test("myTest") { + assertEquals(sizeOf[Int], 4.as[SizeT]) + } +``` + +This tends to force the test suite to actually reveal the exception that's breaking it, and will help one fix the issue in question. + +When a test suite continues forever, the cause is usually the same, but with regards to a propertyBased test: + +```scala +trait MySuite(s: Slinc) extends ScalacheckSuite: + import s.{*, given} + + property("myProperty") { + forAll{ + (i: Int) => + Scope.confined{ + val ptr = Ptr.blank[CInt] + + !ptr = i + assertEquals(!ptr, i) + } + } + } +``` + +should be changed to + +```scala +trait MySuite(s: Slinc) extends ScalacheckSuite: + import s.{*, given} + val ptr = Ptr.blank[CInt] + + !ptr = 4 + !ptr + + property("myProperty") { + forAll{ + (i: Int) => + Scope.confined{ + val ptr = Ptr.blank[CInt] + + !ptr = i + assertEquals(!ptr, i) + } + } + } +``` \ No newline at end of file diff --git a/core/docs/_docs/index.md b/core/docs/_docs/index.md new file mode 100644 index 00000000..82759079 --- /dev/null +++ b/core/docs/_docs/index.md @@ -0,0 +1,46 @@ +--- +layout: index +--- + +Slinc is a Scala 3 library that allows users to interoperate with C code via Java 17+'s foreign api. + +It's designed to make use of Scala's type system, macros, and runtime multi-stage programming to make bindings to C from Scala. + +## Quickstart + +Slinc is published to Maven Central for Scala 3. It is built to take advantage of the cutting edge of Scala functionality and features, so your project will most certainly need to track the latest Scala version if you want to use the latest version of Slinc. Currently, the Scala version in use is `3.2.1`. + +### SBT setup + +In your `build.sbt`: + +```scala +libraryDependencies += "fr.hammons" %% "slinc-runtime" % "0.1.1-66-a2fa26-DIRTYd1a2c450" +//if forking and on Java 17 +javaOptions ++= Seq("--add-modules=jdk.incubator.foreign", "--enable-native-access=ALL-UNNAMED") +``` + +in .jvmopts in the root of your build: +``` +--add-modules=jdk.incubator.foreign +--enable-native-access=ALL-UNNAMED +``` + +For additional setup instructions, please refer to the configuration page. + +Once you have your build system set up, you can create a new file and write the following code: + +```scala +import fr.hammons.slinc.runtime.{*,given} + +case class div_t(quot: Int, rem: Int) derives Struct + +object MyLib derives Library: + def div(numer: Int, denom: Int): div_t = Library.binding + +@main def calc = + val (quot, rem) = Tuple.fromProduct(MyLib.div(5,2)) + println(s"Got a quotient of $quot and a remainder of $rem") +``` + +This library relies on the user importing the runtime from `fr.hammons.slinc.runtime`. \ No newline at end of file diff --git a/core/docs/_docs/reference/jdk-support.md b/core/docs/_docs/reference/jdk-support.md new file mode 100644 index 00000000..69aa4afd --- /dev/null +++ b/core/docs/_docs/reference/jdk-support.md @@ -0,0 +1,21 @@ +--- +title: "Support Matrix" +--- + +Slinc currently supports the following JDKs and Platforms: + +|JDK|Platform|Arch|Support| +|---|--------|----|-------| +|17| Windows |x64|✓ | +|17| Linux | x64 | ✓ | +|17| MacOS | x64 | ✓ | +|17| MacOS | AArch64| ? | +|19|Windows| x64|✓ | +|19|Linux|x64|✓ | +|19|MacOS|x64|✓ | +|19|MacOS|AArch64|?| + + +* Eclipse Temurin 17.0.4.1 +* Eclipse Temurin 19 + diff --git a/core/docs/_docs/reference/overview.md b/core/docs/_docs/reference/overview.md new file mode 100644 index 00000000..b268329e --- /dev/null +++ b/core/docs/_docs/reference/overview.md @@ -0,0 +1,6 @@ +--- +layout: index +title: Reference +--- + +Reference information here TODO \ No newline at end of file diff --git a/core/docs/_docs/reference/usage.md b/core/docs/_docs/reference/usage.md new file mode 100644 index 00000000..48b649bb --- /dev/null +++ b/core/docs/_docs/reference/usage.md @@ -0,0 +1,89 @@ +--- +title: Usage +--- + +## Introduction to Library Definitions + +All bindings by Slinc take place in library objects, groups of like methods that reflect C functions. When defining a library object, name is not particularly important. One merely defines an object that derives `Library`. Method bindings are done via a method binding with a name matching the C function in question, and parameters with types that match the C function in question. + +```scala +object StdLib derives Library: + def abs(i: Int): Int = Library.binding +``` + +The above defines a binding to the C standard library's `abs` method. Since the C definition of `abs` is defined like `int abs(int i)`, the scala version of the method is defined with a single input that has the type `Int` and the return type `Int`. The method is defined with `Library.binding` which is a macro that connects the inputs and outputs to the C world. + +## Types + +In Slinc, you are allowed to use most of the primitive types to bind to C functions. However, these types don't all match C types. In fact, while the primitive types work the same on all platforms within Java, they do not work the same on all platforms in C. + +For example, take the `labs` method from the standard library: `long labs(long l)` + +Looking at this function, one might be tempted to write the following binding: + +```scala +object StdLib derives Library: + def labs(l: Long): Long = Library.binding +``` + +However, while `Long` is always 64-bit on the JDK, it's 32-bit in C on Windows x64, meaning that this binding will fail to work on Windows. It is for this reason that Slinc defines the C types: + +|Slinc|C|JVM| +|-----|-|----| +|CChar|char|Byte| +|CShort|short|Short| +|CInt|int|Int| +|CLong|long|?| +|CLongLong|long long|Long| +|CFloat|float|Float| +|CDouble|double|Double| + +These base C types are meant to mirror C, and currently most have JVM equivalents. + +Now with with table, we have a much more appropriate binding for `labs` + +```scala +object StdLib derives Library: + def labs(l: CLong): CLong = Library.binding +``` + +Now that we have the binding, let's try to use it: `StdLib.labs(???)` + +Looking at the table, the equivalent of `CLong` on the JVM is `?`. That is, it doesn't actually have an equivalent primitive type. On Windows x64, `CLong` is 32-bits wide, making it the equivalent of `Int`, but on Linux and Mac, it's 64-bits wide and is the equivalent of `Long`. In order to use `CLong` we need to convert from a Java primitive. The `as` and `maybeAs` extension methods on the primitives serve this purpose. + +* `4.as[CLong]` - The result type is `CLong`, and the conversion is certain to succeed. +* `4.maybeAs[CLong]` - The result type is `Option[CLong]` and the conversion may fail (indicated by `None`). + +Since the C standard says that `long` is at least 32-bits long, `as` is available for `Int`, `Short`, and `Byte` types. All other primitive integer types can convert to `CLong` via the `maybeAs` method. + +The C types are meant to be analogues to the primitive types defined for C. In the table above, a number have equivalents to JVM types right now, but that may change in future versions of Slinc. If your wish is to write platform independent bindings to C libraries, then you should use the C types and forgo the standard JVM primitives. Usage of the standard JVM primitives will make your bindings brittle and platform specific at some point. + +## Pointers + +Pointers are represented in Slinc with the `Ptr` type. For example, `Ptr[Int]` is a pointer to native memory that should be readable as a JVM Int. + +The pointer class' operations are powered by three type classes: + +* `LayoutOf` - this type class provides layout information about the type in question, if it exists. +* `Send` - this type class shows how to copy data of type `A` from the JVM into native memory. +* `Receive` - this type class shows how to copy data of type `A` from native memory into the JVM heap. + +The list of operations available on a `Ptr[A]`: + +* `apply(n: Bytes)` - offsets the pointer by `n` bytes +* `to[A]` - casts the pointer to type `A` +* `!ptr` - dereferences a pointer (requires `Receive[A]` be defined) +* `!ptr = 5` - copy data into a pointer (requires `Send[A]` be defined) +* `Ptr.blank[A](n: Int)` - create a blank space in native memory that can store `n` consecutive instances of `A` (requires `LayoutOf[A]` be defined) +* `Ptr.copy[A](a: A)` - create a copy of `a` in native memory (requires `Send[A]`) +* `Ptr.asArray(size: Int)` - attempts to copy the data at the pointer into an Array of size `size` (requires `Receive[A]`). This is a very dangerous operation that can crash your program if you don't have all the data you need. + +## Structs + +The analog for C structs in Slinc are case classes that derive the `Struct` type class. An example analog for the div_t struct in the C standard library is defined as such: + +```scala +case class div_t(quot: Int, rem: Int) derives Struct +``` + +These struct analogs can be composed with any type that has a Send and/or Receive defined for it. \ No newline at end of file diff --git a/core/docs/_layouts/index.html b/core/docs/_layouts/index.html new file mode 100644 index 00000000..0bbee9b8 --- /dev/null +++ b/core/docs/_layouts/index.html @@ -0,0 +1,12 @@ +

{{ page.title }}

+ +{{ content }} + +

Table of Contents

+ \ No newline at end of file diff --git a/core/docs/sidebar.yml b/core/docs/sidebar.yml new file mode 100644 index 00000000..7c2a0f38 --- /dev/null +++ b/core/docs/sidebar.yml @@ -0,0 +1,11 @@ +index: index.md +subsection: + - title: Reference + directory: docs/reference + index: reference/overview.md + subsection: + - page: reference/usage.md + - page: reference/jdk-support.md + - title: Contributing + directory: docs/contributing + page: contributing/contributing.md \ No newline at end of file diff --git a/runtime-test/test/src/fr/hammons/slinc/Bindings.scala b/runtime/test/src/fr/hammons/slinc/Bindings.scala similarity index 100% rename from runtime-test/test/src/fr/hammons/slinc/Bindings.scala rename to runtime/test/src/fr/hammons/slinc/Bindings.scala diff --git a/runtime-test/test/src/fr/hammons/slinc/Transfers.scala b/runtime/test/src/fr/hammons/slinc/Transfers.scala similarity index 100% rename from runtime-test/test/src/fr/hammons/slinc/Transfers.scala rename to runtime/test/src/fr/hammons/slinc/Transfers.scala