interop
Java interop, worked: the JDK's own static factories, laying out a build.
examples/interop/interop.dawn
# Java interop, worked: the JDK's own static factories, laying out a build.
#
# `Path.of` and `List.of` are declared on *interfaces* and both take a varargs
# tail (docs/spec.md §9.1, §9.3).
#
# Run: dawn run examples/interop/interop.dawn -- [project-root]
use java "java.nio.file.Path"
use java "java.util.List"
use java "java.lang.String"
use java "java.util.regex.Pattern"
# A static method declared on an interface is called with `invokestatic` against
# an `InterfaceMethodref`, a constant the class-file format admits only from
# major version 52 on. So this file is also the corpus that keeps that path
# reachable rather than merely believed (docs/jvm-base-plan.md §5.11).
const MODULES: List[String] = ["lexer", "parser", "codegen"]
# `<root>/<module>/src`, spelled by the platform's own path joiner instead of by
# string concatenation. The tail of `Path.of(first, more...)` is varargs, so the
# segments after the first are packed into a `String[]`. Java methods return
# `Option` -- null is stopped at the boundary (§9.2) -- hence the two `!`.
fn src_dir(root: String, module: String) -> String !io = {
let p = Path.of(root, module, "src")!
p.toString()!
}
fn jar(root: String, module: String) -> String !io = {
let p = Path.of(root, module, "build", module ++ ".jar")!
p.toString()!
}
# The `-cp` a JVM would be launched with. `List.of(a, b)` resolves to the
# fixed-arity `of(E, E)` (phase 1); a third element would pack into `of(E...)`
# instead (phase 2). Either way `String.join` reads the elements back in the
# order they were written.
fn classpath(root: String) -> String !io = {
let jars = List.of(jar(root, "lexer"), jar(root, "parser"))!
String.join(":", jars)!
}
pub fn main() -> Unit !io = {
let root = match args().get(0) {
Some(r) -> r
None -> "."
}
for m in MODULES {
println(src_dir(root, m))
}
println(classpath(root))
}
test "Path.of packs the varargs tail into a String[]" {
let two = Path.of("a", "b")!
assert two.toString()! == "a/b"
assert src_dir("/tmp", "lexer") == "/tmp/lexer/src"
}
test "List.of keeps its elements and their order" {
let three = List.of("a", "b", "c")!
assert three.size() == 3
assert String.join(",", three)! == "a,b,c"
}
test "the classpath is the jars, in order, colon separated" {
assert classpath("/p") == "/p/lexer/build/lexer.jar:/p/parser/build/parser.jar"
}
test "an array already in hand fills the varargs slot as it stands" {
# `Pattern.split` hands back a `String[]`, and `join(CharSequence, CharSequence...)`
# *is* `join(CharSequence, CharSequence[])`: phase 1 takes the array whole, so
# nothing is repacked. Phase 2 would wrap it in a `String[][]` and match no
# candidate, which is why this line resolving at all is the evidence.
let comma = Pattern.compile(",")!
let parts = comma.split("a,b,c")!
assert String.join("-", parts)! == "a-b-c"
}