I added vulkanmod support for all supported versions above 1.21.4, it uses the same backend as the 26.2 vulkan renderer but has some extra handling for vulkanmod
I added vulkanmod support for all supported versions above 1.21.4, it uses the same backend as the 26.2 vulkan renderer but has some extra handling for vulkanmod
Added Vulkan support!
This was pretty hard because skiko does not come with vulkan support by default.
However, there is a pull request that adds vulkan support.
So I created git submodules for skia and skiko, made a setup script that automatically applies the vulkan patches, builds and publishes to maven local.
Then I had to fight with a lot of issues(some of them I couldn’t even debug because it was happening in native code), but I got it working ![]()
Added support for 26.2, only for the OpenGL renderer.
This is because skiko(backend of jetpack compose) does not support vulkan.
Updated gradle and dependencies.
Added support for 1.21.11 and 26.1.2
Did some cleanup for the mixins
Added generics support, which allows the user to define generic functions/structs that apply with every type, for example:
warp <T> void forEach(T[] a, (T) -> void action) {
for(T t in a) {
action(t);
}
}
Depending on what type you used to call this, a new version of forEach gets generated, if you called it with an integer:
warp void forEach@int(int[] a, (int) -> void action) {
for(int t in a) {
action(t);
}
}
Of course I also allow this on structs:
struct Triangle<Num>(Num x1, Num y1, Num x2, Num y2, Num x3, Num y3);
Also did some small changes:
More abstractions please!
Added dynamic function dispatch which allows for functions to take in
other functions as arguments, of course, this isn’t possible at all in
normal scratch, so actually FunctionLiteral gets turned into an
integer and a custom dispatch function gets created for every function
literal’s signature which allows for a binary search to call the actual
function
Also made HeapConversion reuse stack slots instead of allocating one
for every variable(mostly by AI)
Refactored the entire type system to use a sealed interface instead of an unsafe single class
Nothing much to say, this doesn’t affect the input code or the output project, I did just cleanup
Did more syntax improvements
Added ++ and – support:
index++;
Added type inference support:
auto a = Triangle(5, 15, 60, -120, 240, 67); //type automatically becomes Triangle
auto a = func(); //type automatically becomes the return type of func
Scratcher is at 100 commits! (Now 104 cause all of these refactors are different commits)
Completely refactored all translation steps to use the ASTVisitor.
Also did type resolving cleanup.
Function Reachability: Went from 117 to 32 lines
Function expression lowering: Went from 439 to 293 lines
Re Parse Local Variables: Went from 34 to 27 lines
Remove Empty Allocations: Went from 97 to 79 lines
ConvertToHeapAccess: Biggest refactor of this whole devlog, went from 500 lines to 418, split into 5 files
Fixed a lot of bugs with when expressions, also got deepseek to fix some of them
Added If expressions, which allow using if statements inside other expressions, for example, here is some previously invalid code that is now valid:
int max = if (a > b) a else b;
str message = if (score >= 90) {
looks::say("something");
"You win!"
} else {
looks::say("something");
"You lose :("
}
triangles[15] = Triangle(
utils::random(-240, 240),
utils::random(-180, 180),
utils::random(-240, 240),
utils::random(-180, 180),
utils::random(-240, 240),
if(mode == TriangleRenderMode.OFF) {
5
} else {
triangles[14].y3
}
);
These still get lowered properly and just look like normal if statements in the final scratch code
Added more syntax improvements!
Else if: Added support for else if, which replaces big blocks
//Before:
if(triangles[14].y1 >= 15) {
looks::say(">=15");
} else {
if(triangles[14].y2 >= 155) {
looks::say("<=15");
} else {
looks::say("Both false!");
}
}
//After:
if(triangles[14].y1 >= 15) {
looks::say(">=15");
} else if(triangles[14].y2 >= 155) {
looks::say("<=15");
} else {
looks::say("Both false!");
}
Compound assignment: Added support for compound assignment
//Before:
triangles[15].y3 = triangles[15].y3 + 5;
//After:
triangles[15].y3 += 5;
Enums: Added support for enums, which are just fancy integer wrappers
enum TriangleRenderMode(OFF, RENDER, ITHINK);
TriangleRenderMode mode = TriangleRenderMode.OFF;
When: Added support for when expressions/statements that allow for matching a subject:
//Before:
if(answer == "off") {
mode = TriangleRenderMode.OFF;
} else if(answer == "render") {
mode = TriangleRenderMode.RENDER;
} else {
mode = TriangleRenderMode.ITHINK;
looks::say("If thinking is your power, what are you without it?");
return;
}
//After:
TriangleRenderMode mode = when(answer) {
"off" -> TriangleRenderMode.OFF
"render" -> TriangleRenderMode.RENDER
else -> {
looks::say("If thinking is your power, what are you without it?");
TriangleRenderMode.ITHINK;
}
};
Tried to do turbowarp only return optimization, wasn’t able to make it very good… reverted it
Improved the structure of CompilationConstants
Added string::split and string::substring
Made some of the list functions inlined (itemAt, clear, length)
Made the garbage collector treat top level variables as roots, this prevents top level variables from getting freed
I added 2 modes to this
Added a mark and sweep garbage collector to my language, it walks the roots, marks every used object, then frees all unused objects!
Most impressive part of this(in my opinion) is that most of the garbage collector is actually written in my language
Added a name parameter to alloc which acts as runtime type metadata, soon to be used.
Added more compiler sugar for lists:
list[index] replaces list::itemAt(list, index)
list[index] = ...; replaces list::replace(list, item, index)
for(type name in list) {} replaces
int len = list::length(list);
int i = 0;
while (i < len) {
type name = list::itemAt(list, i);
i = i + 1;
}
(i am not proud of the shitcode that was used to get for(type name in list) working)
Added list support! A very barebones implementation that allows the user to create, add to, read from and delete items from lists!
Made “PromoteToGlobals” which is a more powerful version of OptimizeToGlobals which allows for recursive functions to have their locals turned into globals aswell
This is something called liveness analysis, the compiler looks at the code paths and decides at which points a variable is “live”, which means it can’t get overwritten by a recursive call
Example:
warp int fib(int n) {
return 0 if n <= 0;
return 1 if n == 1;
if (n == 2) {
int b = n * 2;
return otherFunc(b);
}
return fib(n - 1) + fib(n - 2);
}
In this function the “b” variable can be turned into a global because it cannot get overwritten by a recursive call, its used too early for that to happen
Did some compiler testing
Fixed function inlining bugs
Fixed a single use assignment bug
Created a triangle library that allows for triangle rendering using the pen
Borrowed the “return expr if(cond)” syntax from ruby
Implemented Tail call optimization which optimizes away recursion on tail calls
Example:
Before:
str count(int amount) {
return "Done!" if amount == 1;
return count(amount - 1);
}
After:
str count(int amount) {
int tco@argument@amount = amount;
while (true) {
if (tco@argument@amount == 1) return "Done !";
tco@argument@amount = tco@argument@amount - 1;
}
}