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