Excise hardcoded Vec4F
Making variable-length vectors isn't too hard maybe
Refactored function type a little.
A small experiment in making a toy programming language that compiles to SPIR-V shaders for Vulkan.
Basically, I want a compiler targeting SPIRV that is easier to embed than shaderc, and I want to have a mostly-pure-functional shading language (because shaders are, you know, basically pure functions already), so here we are. I'll probably never finish it.
So far it's only been tested on Linux. Probably will work fine on whatever, since there's no platform-specific dependencies.
Running unit tests requires the spirv-val
command line program from the
official Khronos SPIR-V tools; on Debian you just have to run
apt install spirv-tools
. glslang-tools
may also be useful.
To actually see specifics of what's going on it may also be useful to
test with cargo test -- --nocapture --test-threads=1
.
This is an experiment on making something compile to SPIR-V more than anything else, so. It's going to be a strongly-typed, pure functional language that probably looks kinda like ML. But I'm starting with the IR and backend for now, 'cause writing parsers is frustrating and slow.
Actually, having no mutation really doesn't make anything harder, since SPIR-V is a SSA form anyway.
Though traditionally the way to handle loops in such language is via recursion, and shaders generally can't be recursive, so that's going to be tricky. We're probably going to either have to introduce loops with attendant mutation (fake or real), or only allow recursion that can be flattened into loops. Though the SPIR-V spec is oddly mostly silent on whether recursion is allowed...
I guess it's not even a real functional language yet either, since it doesn't yet implement functions as values, though it looks like SPIR-V can support it.
Actually to do:
To think about:
To compare against code generated by a (hopefully) known-valid compiler:
# -G for GLSL semantics, -V for Vulkan semantics.
glslangValidator -V test.frag.glsl; spirv-dis frag.spv
To not do YET:
ML-y syntax or Lispy syntax?
Let's go with ML-y to start, just to see what it looks like.
Really I'm not sure how to handle math operations; we have no generics
or traits, so making an Add
trait doesn't really work. However, we
also have no type inference, so we don't necessarily need OCaml style
+
vs +.
Seems like we can either do it C style and overload math
operators as a special case, which I dislike but which is easy in
practice, or we can do it ML style and overload nothing, which is
simpler but annoying to write.
But we could make it so that adding integers is +
, floats is +.
,
vectors is +/
and matrices is ++
. Which is almost too cute to
resist. For now though, undefined.
-- Yes, Lua style comments
-- just to mess with people.
/- Because why not -/
-- DECLARATIONS
-- Anyway, two types of decl's, functions and structs.
fn foo x: F32, y: F32 -> bool
-- ...
end
-- Types must start with a capital letter
struct SomeStruct
x: F32,
y: F32,
end
-- EXPRESSIONS
-- Floats must have a decimal point
let foo: F32 = 10.0
-- Math
x + y
x - y
x * y
x / y
x % y
-- Comparison
x == y
x != y
x > y
x >= y
x < y
x <= y
-- Logic, on booleans. No bitwise stuff yet.
x or y
x and y
x xor y
not x
-- Blocks
do
...
end
-- Function calls
foo(x, y, z)
-- Structure literals
SomeStruct { x: 1.0, y: 2.0 }
-- Pattern matches
match foo
| 1.0 -> foo * 2.0
| 2.0 -> foo / 2.0
| _ -> -9999.0
end
-- Destructuring matches
-- variables always are lowercase.
let SomeStruct { x, _ } = foo
-- x is now bound