Up to Main Index Up to Journal for March, 2023 JOURNAL FOR SATURDAY 25TH MARCH, 2023 ______________________________________________________________________________ SUBJECT: State of things + a peek behind Mere’s curtain DATE: Sat 25 Mar 15:00:38 GMT 2023 [ Staring into a blank page in my editor and need to write something. Let’s try an intro and see where that leads… ] With respect to the journal, this year has started off pretty poorly. It’s now late March and this is only the fourth entry :( What with COVID, wall-to-wall diarrhoea for over a week, then an annoying cough and cold that just would not break. Now I’m left exhausted all the time. I have tried poking at WolfMUD and Mere a bit but my usual enthusiasm for programming just hasn’t been there. Mostly I’ve been programming for work and that’s been it. Isn’t it funny how illness can have odd effects? I was excited about the new version of Mere and everything was going pretty well. Now I look at it and think “Damn!, am I just creating another pile of crap?” :| This has also taught me not to do anything rash, like delete all my code, while under the influence of being ill. [ Well that was a gloomy intro. Lets try to think of some good stuff to add… ] I’ve had to force myself to plod on with Mere. Writing all the tests is really soul sucking. The tests are very useful, so I’m grateful I’ve spent the time writing them. I think I have four built-in functions left to implement: input, replace, sort and substr. That should put me on par with the last release of the first version. Technically only input was in the first version, and that wasn’t available via the ICE. The other three built-ins were not released. [ Maybe add some code examples here? Readers like code examples… ] Currently there are two gaping holes in Mere. No functions and no For loops. For loops are currently written using Goto: >cat loop.mr x=1 xloop: println x if x++ <= 3; goto xloop >mere loop.mr 1 2 3 > It would be nice to be able to write this as: for x=1; x <= 3; x++ { println x } Then there are functions. At the moment Mere only has horrible subroutines. Why horrible? No scoping, everything is global. There are no parameters or returned values, everything is passed via the global scope :( Usually I name parameters and return values using the subroutine label as a prefix: >cat reverse.mr msg_text="abc"; gosub msg; println msg_result msg_text="xyz"; gosub msg; println msg_result exit msg: msg_result = "" l = len(msg_text) lloop: msg_result += msg_text[l--] if l > 0; goto lloop return >mere reverse.mr cba zyx > Implementing both of these will take time. The simpler of the two to implement will probably be functions. The For loops are more complicated and will require the compiled code to be rewritten. Apart from the odd token rewrite I’ve avoided replacement with generated code. The layout of a typical For loop might be: for init; test; increment { body } When generated it would need to be something more like: init block_start: if !test; goto block_end body inc goto block_start block_end: This is not an insurmountable problem. However compiled Mere code is just a stream of tokens. There is nothing complex or fancy like an abstract syntax tree. For example, consider a simple program to flip a coin: >cat coin.mr trace true coin=[int](1 "Head", 2 "Tail") println coin[rnd 2] First the source is tokenized: 0 | trace true ; 3 | coin = [ int ] ( 1 "Head" , 2 "Tail" ) ; 16 | printf "%s!\n" coin [ rnd 2 ] ; Then the tokens interpreted into reverse polish notation: 0 | … true trace ; 4 | coin … 1 "Head" 2 "Tail" , [int] = ; 14 | … "%s!\n" coin 2 rnd [ printf ; The reverse polish tokens are then symbolized: 0 | O[…] b[true] F[trace] O[;] 4 | V[0x01:? coin] O[…] i[1] s["Head"] i[2] s["Tail"] O[,] F[[int]] O[=] O[;] 14 | O[…] s["%s!\n"] V[0x01:? coin] i[2] O[rnd] O[[] F[printf] O[;] If you look at the raw structure of the final compiled code[1] you would get: mere.stack{"…", true, "trace", ";", 0x1, "…", 1, "Head", 2, "Tail", ",", "[int]", "=", ";", "…", "%s!\n", 0x1, 2, "rnd", "[", "printf", ";"} Note that this does not show the type information. This is shown above in the symbolized listing as: operators O[], built-ins F[], integers i[], etc. When run, with tracing enabled, we can see the reverse polish being executed: >mere coin.mr 3 ; | O[…] 5 … | V[0x0001:? coin] 10 , | V[0x0001:? coin] O[…] i[1] s["Head"] i[2] s["Tail"] 11 [int] | V[0x0001:? coin] O[…] i[1] s["Head"] i[2] s["Tail"] 12 =im | V[0x0001:? coin] im[[int](1 "Head", 2 "Tail")] 13 ; | im[[int](1 "Head", 2 "Tail")] 14 … | 18 rnd | O[…] s["%s!\n"] V[0x0001:im coin] i[2] 19 [iim | O[…] s["%s!\n"] V[0x0001:im coin] i[2] 20 printf | O[…] s["%s!\n"] s["Tail"] Tail! 21 ; | [ Time to wrap before we start to waffle… ] This is just a quick look behind the curtain. I plan on doing a more in-depth look at how Mere was built and how it works. I’ll explain my design decisions, the mistakes made and maybe some pointers on writing your own programming language. -- Diddymus [1] Note that the operator and built-in tokens are currently kept as strings. This is inefficient especially for map lookups. It keeps the development simple. The plan is to encode them as integers eventually. Up to Main Index Up to Journal for March, 2023