#badlang — Public Fediverse posts
Live and recent posts from across the Fediverse tagged #badlang, aggregated by home.social.
-
Ok I think I found a good name for my programming language. It's short and sweet and I haven't seen it used anywhere for other PL projects:
The Oni programming language
Named after the evil japanese yokai. Seems in-theme with the previous "badlang" name.
I've acquired oni-lang.com and will be updating my repos to use the new name soon. Perhaps this is a good opportunity to start adding a website and documentation.
-
Having a lot of fun with this, I sense a new toy project incoming :)
This is using the wavreader from the badlang standard library with a custom playdate IO.Reader and a simple sample playback DSP code. All written in my own little language.
-
Without too much trouble I managed to run Badlang code on the Playdate. Just had to override the panic function to use the pd system log and to create some wrapper functions.
I also added a custom allocator, meaning I can use most of the relevant stuff from the standard library. Here is shown a directory list, a file being read (and the written) and timing information. Additionally I can draw using the system drawing functions (for text and sprites) and directly on the framebuffer (as seen on the grid behind).
Perhaps I’ll have a bit more fun with this soon :)
-
Started exploring Result::(Error, Val) types and implemented a `tryor` operator (??). The previous `try` operator (?) would unwrap or return the default zeroed value for the current function. With this new operator we can perform some extra action before returning and give our desired return type.
Equivalent to:
let a = funcall() ?? ret_expr
let a = {
let tmp = funcall()
if not tmp.ok() then return ret_expr
tmp.val()
}Since `ret_expr` is an expression, you could log a message before returning or something like that.
Pretty happy with this approach, I don't like to just blindly propagate an error, it needs to get handled somewhere or transform across type boundaries, which this should be able to handle.
-
Back home. Been doing some light programming on the side, namely bugfixing, adding support for typed enums (still only integer types, but this way you can stuff enums into structs and arrays and control their size) and exploring adding type aliases.
My initial implementation is super simple, just literally an alias so any function that takes the original type would also take the alias and vice-versa. Starting adding a more complex implementation where aliases are new types that could be casted back and forth between the original and alias implementation. This would become a hard typecheck failure if an alias is used instead of the original type and would enable having specific methods for aliased types.
Unsure if the complexity is worth the cost for this particular feature. Thoughts?
-
Following along Beej's networking guide [1] and implementing unix sockets bindings for Badlang. I thought it would be fun to make a simple HTTP server with this!
The C sockets API is not very ergonomic so I'm planing some improvements to it, but gotta play a bit more with it first.
-
Hello folks, I've updated the README of #badlang and would love to have some feedback (boost welcome).
Still need to work on the documentation and language reference but I'm trying to touch on the general summary of the project.
Let me know what you think!
-
I'm pretty happy with the current language at the moment, there may be a couple of extra small features I will consider adding before doing an official release. Most of the work now will go towards adding some stdlib stuff, and keep playing around with it, finding rough edges and having fun using it!
For now I've made it so #! is also a comment, that way I can use source files directly as scripts which I think it's pretty neat.
I've also added some initial tracing allocators to be able to log in allocations and de-allocations and added some basic logging functionality with a date and prefix tag.
-
I was thinking on embedding libtcc to be able to use TCC to run .bad files as if they were scripts.
But what if instead I just provide a simple bash script that serves the same function if installed alongside the badc and tcc executables? This keeps badc entirely dependency free other than libc, and that could also be removed. A fully static executable build it's already possible with musl, so it would be nice to be able to just provide a binary and the stdlib for releases.
-
Back from my trip to BCN and back again to work on the compiler.
Did some code cleanup and finally added type inference for struct, union and choice type literals.
Naturally, type inference is not always possible in which case we can always use polytype annotations as before, but this is much more convenient for when it is.
-
I added the following compiler directives:
#flag to set a compiler flag, like a #define in c but without value
#def set a compiler flag with a compile time string value.
#get gets a compile time flag value
This in combination with a —def parameter on the compiler enables me to finally have a compile time default path for the stdlib, so that I can do make install and the compiled binary will fetch the path of the system stdlib instead of the dev path.
I may expand this in the future for other constants, but this is sufficient for now. I would like to keep comptime stuff at a minimum.
-
Today I expanded on the direct to backend compiler directives:
#emit puts the given string directly on the source code as a LinearOp
#funattr adds function attributes to the current function
#global puts the string into the top of the generated file
#local puts the string into the top of the current functionWith these in place you can do things like adding linear assembly (useful to insert optimization fences or other shenanigans), hookup instrumentation, and to configure your functions as you would with a C compiler (add always_inline, force loop unrolls, put a function into a given section, etc.). I think these form a base that could work for most or all backends I can think of, so they are not limited to the current C one.
-
Today I expanded on the direct to backend compiler directives:
#emit puts the given string directly on the source code as a LinearOp
#funattr adds function attributes to the current function
#global puts the string into the top of the generated file
#local puts the string into the top of the current functionWith these in place you can do things like adding linear assembly (useful to insert optimization fences or other shenanigans), hookup instrumentation, and to configure your functions as you would with a C compiler (add always_inline, force loop unrolls, put a function into a given section, etc.). I think these form a base that could work for most or all backends I can think of, so they are not limited to the current C one.
-
Today I expanded on the direct to backend compiler directives:
#emit puts the given string directly on the source code as a LinearOp
#funattr adds function attributes to the current function
#global puts the string into the top of the generated file
#local puts the string into the top of the current functionWith these in place you can do things like adding linear assembly (useful to insert optimization fences or other shenanigans), hookup instrumentation, and to configure your functions as you would with a C compiler (add always_inline, force loop unrolls, put a function into a given section, etc.). I think these form a base that could work for most or all backends I can think of, so they are not limited to the current C one.
-
Today I expanded on the direct to backend compiler directives:
#emit puts the given string directly on the source code as a LinearOp
#funattr adds function attributes to the current function
#global puts the string into the top of the generated file
#local puts the string into the top of the current functionWith these in place you can do things like adding linear assembly (useful to insert optimization fences or other shenanigans), hookup instrumentation, and to configure your functions as you would with a C compiler (add always_inline, force loop unrolls, put a function into a given section, etc.). I think these form a base that could work for most or all backends I can think of, so they are not limited to the current C one.
-
Today I expanded on the direct to backend compiler directives:
#emit puts the given string directly on the source code as a LinearOp
#funattr adds function attributes to the current function
#global puts the string into the top of the generated file
#local puts the string into the top of the current functionWith these in place you can do things like adding linear assembly (useful to insert optimization fences or other shenanigans), hookup instrumentation, and to configure your functions as you would with a C compiler (add always_inline, force loop unrolls, put a function into a given section, etc.). I think these form a base that could work for most or all backends I can think of, so they are not limited to the current C one.
-
Since yesterday I was looking at some tooling and it's integration with my language I started pondering if it would make sense to add some command line arguments to add instrumentation to functions, like so:
badc --inst-all-fun AAA
--inst-fun-with-prefix foo BBB
--inst-fun-with-suffix foo CCC
--inst-fun-matching foo DDDThis could be used to add timers or profiler hooks to the beginning of a function, and since we have `defer`, also to the end if we wanted to.
-
Now that I have a generic map implementation, I can finally use it to replace the previous Map::(Str, U32) with a Map::(Type, U32). This avoids the creation of unnecessary temporary strings that I was using for type uniqueness determination.
As a result, the semantic analysis now only uses a whooping 0.7MBs of temporary memory instead of 180MB that it was using previously. It's also about 14% faster but I was mainly looking for the reduction in memory usage.
-
Checking out this wonderful GDB frontend[1] by @nakst
Because I can emit line directives in debug mode I'm able to step through my code, set breakpoints and look and registers.
I wonder if there is something similar to #line but for registering locals or function names, otherwise my codegen locals look like _l0, _l1, _t0, etc. Still, pretty useful already!
-
Taking some time to fix some bugs and do a pass of consistency and documentation over the current stdlib. Also started the implementation of a generic Map::(K, V) data structure.
In the current compiler I'm using specific implementations for IntMaps and StrMaps but they are just specialization with a lot of code shared. The new implementation also supports iterators that can be used on foreach loops.
-
Yo dawg I heard you like assembly so I put an assembly in your assembly so that you can assembly while you assembly.
AKA: Experimenting with building a fully freestanding binary from badlang for x86_64, emitting C code directly that emits ASM.
-
Yesterday I did some vim magic to update all of my Raylib bindings to follow the same style convention as the rest of the codebase. May be a minor thing, but it's really nice not to have to depend on the style convention of the library provider IMO as consistency is very important to me.
-
Migrated all of the IO writing to the new IO.Writer formatting. Also added a few more formatting options for std/fmt and an IO.FileWriter implementation of IO.Writer.
Additionally, finally I addressed the issues with unused expressions being typechecked (for example, if we are not assigning the result of an if expression, there is no need for both branches to have to have the same type. This also applies for match expressions.
-
Now that I have a usable std/fmt library I decided to move the backend output to use it, that way I can finally start implementing a direct to file output (-o file.c) instead of relying on stdout.
As a nice side effect, the new implementation ends up being 30% faster, which is quite substantial!
-
Adding float, hex and padded hex to std/fmt.
Another cool thing here is that anyone could extend the formatter for their own applications by doing this anywhere in their scopes:
namespace Fmt {
methods Formatter { ... }
}So custom structs formatting or serializers should be easy to build with this design.
-
Here is a proof of concept for the stdlib formatting functionality. No varargs, no macros, no interfaces. Instead, a Formatter struct is used in a monadic way, being passed in the chain until a formatting delimiter is detected.
Here I'm using {}, but I may swap it to a single character instead (% maybe?) and do all the formatting in methods instead? So for example for floating point you could have a methods like this:
fun float(fmt: @Formatter, f: F64, precision: Int)
Which will use N digits of precision for it. The same could be done for string padding and other such things.
-
Experimenting with implementing my own buffered IO implementation on top of the write syscall. I'm going with Zig's current approach for IO where one would pass an IO Writer vtable around with some context information added.
Starting at a bufsize of 128 my implementation is faster that calling printf("%s.*"...), becoming 7-8x faster with larger buffer sizes (4 to 8K).
Pretty happy with this!
-
Last #DecemberAdventure day, but work won't stop after today, have a really long trip ahead still, and will celebrate new year's on the plane.
In the meantime, I used the new `#emit` directives to move all the stdlib specific code from the backend into std and added the option to compile without main to create standalone lib code that could run on any target with a crt. I also added raw string literals that extend until (and including) the newline.
I would like to fully get rid of the need for libc, but I'm not familiar enough with macos syscalls to start writing assembly for those, would probably do that on my linux machine after I'm back home.
-
Some more #DecemberAdventure work from the bus:
- Improved codegen to avoid generating code for unused functions.
- Added the `#if` compiler directive for conditional compilation based on a given --flag.
- Added the `#error` compiler directive to ensure we have a way to signal a compilation error in some path (for example, unimplemented library functions for a given OS and such).
- Added the `#emit` compiler directive to be able to generate code directly on the backend verbatim. Now #badlang is a C macro assembler lol.Here is everything together.
-
Finally adding struct literals. Initially I was going to use a prefix to denote compoun literals, like `#Vec(x = 1, y = 2)` but I found them aesthetically unappealing.
So alas, we overload the parenthesis syntax a bit (though I was already doing this for sum type literals anyway).
Also going with parens instead of curly braces. Technically parens mean grouping in the language, so it's somewhat consistent with other use cases.
-
Finally adding struct literals. Initially I was going to use a prefix to denote compoun literals, like `#Vec(x = 1, y = 2)` but I found them aesthetically unappealing.
So alas, we overload the parenthesis syntax a bit (though I was already doing this for sum type literals anyway).
Also going with parens instead of curly braces. Technically parens mean grouping in the language, so it's somewhat consistent with other use cases.
-
Finally adding struct literals. Initially I was going to use a prefix to denote compoun literals, like `#Vec(x = 1, y = 2)` but I found them aesthetically unappealing.
So alas, we overload the parenthesis syntax a bit (though I was already doing this for sum type literals anyway).
Also going with parens instead of curly braces. Technically parens mean grouping in the language, so it's somewhat consistent with other use cases.
-
Finally adding struct literals. Initially I was going to use a prefix to denote compoun literals, like `#Vec(x = 1, y = 2)` but I found them aesthetically unappealing.
So alas, we overload the parenthesis syntax a bit (though I was already doing this for sum type literals anyway).
Also going with parens instead of curly braces. Technically parens mean grouping in the language, so it's somewhat consistent with other use cases.
-
Finally adding struct literals. Initially I was going to use a prefix to denote compoun literals, like `#Vec(x = 1, y = 2)` but I found them aesthetically unappealing.
So alas, we overload the parenthesis syntax a bit (though I was already doing this for sum type literals anyway).
Also going with parens instead of curly braces. Technically parens mean grouping in the language, so it's somewhat consistent with other use cases.