As I look at my previous entry, I realise I barely mentioned anything about the implementation itself. That is probably because while I enjoy writing b-logs, I'm trying to rush the development of BAST because of school stuff, so I cannot dedicate much time to anything other than writing BAST.
Well, let's start by talking a bit about generating MoonBit. First, I cannot just throw a BAST name into MooBit source code and expect it to work. This is mainly because BAST allows kebab-case, which I convert to snake_case. This works, because snake_case is not possible in BAST. The other part to this is that the BAST names could conflict with MoonBit keywords and names used internally in implementation. This can be easily solved by just prepending all names with '_bast_'. This way 'foo-bar' becomes '_bast_foo_bar', which should not conflict with anything.
Another weird quirk of MoonBit is that it wants some stuff indented, but it does not care how much, so I just add an extra space before each line to make the compiler happy. Well, happy... that, and I also disable a lot of warnings. I have already been warned enough.
MoonBit is also not very happy about recursive data structure definitions, that is creating a variable, which contains a lambda which refers to said variable. This needs to be done in steps. Luckily for me, It also has one very nice feature for once, that being the 'init' functions. Yes, plural.
Moonbit has two (as far as I know) special functions. 'main', which must be allowed in the project config file and works as an entrypoint, and 'init', Which is called right after loading, even before 'main'. The nice part about 'init' is that there can be multiple of them, so in each file, I declare all top-level variables as Nils and then assign their values to them in a file-specific 'init' block.
Nice.
First of all, I have still a LOT to learn as far as language design goes. I thought that binding would make compilation easier, but using the same symbol in so many different contexts makes the parser quite confused, which makes adding new binds a pain. Also, at least Menhir cannot look before while parsing, so while you might think that differentiation between
while [foo]:[ bar ]:[ baz ]
and
while [foo]:[ baz ]
is obvious, Menhir cannot guess whether the second block is declaration or body, as it cannot look forward whether there is another block bound or not. This makes me suddenly appreciate Pascal even more as all the verbosity actually makes sense. (well, outside of not abbreviating things, that I still don't forgive)
So yea, how did I solve this? Simple! I just made all declarations require a default value. This, while making the deceleration and code blocks different enough, also prevents me from adding any other syntax, which involves binding values to values directly.
Prebinding is a pain to do for anything outside of '++' and '--' and I think I will do without it.
I have no proper way to separate expressions, outside of them not having any operator between them. This makes unary minus impossible to implement, as it constantly gets confused for infix minus. There are two possible solutions to this:
Requiring whitespace would solve more things, but does not look good, or follow the language philosophy, so I chose the secret third option: use '`' for unary minus. Strange choice, I must admit, but hey, it works, OK!
And yea, how could I forget; the binary is getting quite large. It currently sits at 21KiB while doing pretty much nothing, which is a THIRD of the allowed 64KiB for WASM-4, so yea, some features have to go. These might include:
Like yea, I NEED it to be usable with WASM-4, that is the goal. I'm sure I could fit all the features in if I compiled to WASM directly, but I frankly just don't have neither the skills, nor the time to do that.
So yea, I'm basically marking this project as a failure, but I still want to get it at least somewhere, since I really don't want yet another unfinished project.
Speaking of failures: Arrays! In short, I reserved dot syntax for arrays, which does not work well with decimal numbers (go figure), so I had to do something else. I also need a way to assign to the array. As all my binding attempts failed due to all the things mentioned above, I ended up with read/write constructs.
BAST r [foo 1 2 3] C foo[1][2][3]; BAST w [foo 1 2 3 bar] C foo[1][2][3] = bar;
I'm not happy about this, but I'm excusing it as lists being the primary data structure and arrays being there just for WASM FFI.
I also have to yet handle splitting variable names and such. And also some basic syntax checking, as getting non-existent variable error from MoonBit looks really unprofessional (this is funny, since there is absolutely nothing professional about BAST)
If anything, this just makes me admire LISP and FORTH even more for how they manage to avoid all this mess.
But at least cadr/rdac chains of arbitrary length work! I haven't really said anything useful in this b-log either, right?
I guess this shows that I'm dissatisfied with my work and don't consider it anything worth sharing. I will talk more about OCaml in it's own dedicated b-log.
Huh, I guess this is the 'log' par of b-logs. Just screaming to the ehter about how's it going without any added value.
Current commit: 3e6592a9330f1ed7de772ba2455f018a366c6e2b