Topics

String indirection with offset...

Alex Farlie
 

10 DIM A% 256
   20 start%=A%
   30 B%=A%+256
   40
   50 PROCinitalise
   60 READ A$,fn_name$
   70 REM PRINT LEN(A$)
   80 REM PRINT LEN(fn_name$)
   90 !A%=-1:A%!4=-1: REM list is empty
  100 REM PRINT start%!0
  110 REM PRINT start%!4
  120 link%=8+LEN(A$)+1
  130 REM PRINT link%
  140 start%!0=link%
  150 start%!4=EVAL("^FN"+fn_name$+"()")
  160 (start%)$8="":(start%)$8=A$:PRINT A$
  170 REM PRINT start%!0
  180 REM PRINT start%!4:PRINT "SANITY"
  190 PRINT start%$8
  200 END


Expected output:
SANITY

HI

Actual output

SANITY

<memory address>HI


start% is bracketed  in the code because the interpreter complained about a mistake if it wasn't. 




Richard Russell
 

On Fri, Sep 29, 2017 at 01:02 pm, <alex.farlie@...> wrote:
start% is bracketed  in the code because the interpreter complained about a mistake if it wasn't. 
The string indirection operator $ isn't dyadic, so 'variable$offset' is not valid syntax in BBC BASIC; you have to use $(variable+offset) instead.  Putting start% in parentheses completely changes the meaning of the code because it becomes a label (in BB4W and BBCSDL).  Only the numeric indirection operators ! and ? are (optionally) dyadic.

Since your listed code is incomplete (no PROCinitalise, no DATA statement) it doesn't run anyway.

Richard.

Alex Farlie
 

It helps considerably if you initalise your variables in the RIGHT place. :(

I'm giving up, because I still can't see where this code is screwing up. It won't even print strings out properly :(

   10 DIM A% 256
   30 B%=A%+256
   40 A$=""
      pointer%+=0:pointer%=A%
   50 PROCinitalise
   70 eod%=FALSE
   80 REPEAT
        A$="":fn_name$=""
   90   READ A$,fn_name$
  100   IF A$="*" THEN
  110     eod%=TRUE
          PRINT "No more entires!"
          EXIT REPEAT
  120   ENDIF
  130   PROCregister(A$,fn_name$,pointer%)
  140 UNTIL eod%
  150 END
  160 :
  170 DEFPROCregister(A$,fn_name$, RETURN pointer%)
  180 LOCAL align%,link%
      PRINT A$,fn_name$,~pointer%
      IF !pointer%<>-1 THEN
        ERROR 64,"Bad pointer supplied to registeration function.!":END
      ELSE
  260   REM !P%=-1
  270   REM !P%!4=-1: REM Initalise entry
  280   REM link%=P%+(8+LEN(A$)+1)
  290   REM IF link% MOD 4<>0 THEN      : REM Align on 32bit word boundaries.
  300   REM align%=link% MOD 4
  310   REM link%=link%+(4-align%)
  320   REMENDIF
  330   REM !P%=link%
  340   REM %!4=EVAL("^"FN"+fn_name$+"()")
  350   REM $(P%+8)=A$
  360   REM P%=!P% : REM Return with new dictionary stack pointer.
  370   pointer%=pointer%+8
  380   !pointer%=-1
  390   !pointer%!4=-1
  400 ENDIF
  410 :
  420 DEF PROCinitalise:A%!0=-1:A%!4=-1
  430 ENDPROC
  440 :
  450 DEF FNhi(A%):="hi"
  460 DEF FNbye(A%):="bye"
  470 :
  480 DATA "HI","hi"
  490 DATA "BYE","bye"
  500 DATA "*",""

Richard Russell
 

On Fri, Sep 29, 2017 at 02:29 pm, <alex.farlie@...> wrote:
I'm giving up, because I still can't see where this code is screwing up
Sorry, I am having extreme difficulty following your 'shorthand' posts.  Perhaps you could start at the beginning and describe in detail what it is you are trying to achieve and what it is that isn't working.  I wonder if the word 'link' in your code means that you are trying to implement a linked-list. If so can I suggest that you use structures rather than indirection; the code will be much easier to follow and you are more likely to receive a helpful error message when something goes wrong than a crash.  Indirection is unforgiving because the interpreter cannot do any checking.  Check out this Wiki article.

Richard.

Alex Farlie
 

I'm trying to implement a lookup table...  so that  I can adapt the approach used in the Turtle Logo program used elsewhere into something that BB4W allows for namely, function pointers.  The intention of PROCregister is to link text strings representing COMMAND names with the PROC or FN that handles them.   The next portion after having worked out this, is how to do the parsing of commands with additional parameters...

The Command table in the above example is somewhat simplistic..   but I was trying to start somewhere (and failing miserably).

In a full blown interpreter, you'd not only have to implement a command table storing addresses, but parameter lists as well. ( Internally this is what BB4W does.) .  If you think a linked list approach would work better I'm willing to try it.
(I was using indirection as I was eventually planning on back-porting the code to say BASIC V on RISC OS) which as yet  doesn't have the structure syntax in BB4W (although someone was working on it unofficaly I heard.).

I'm going to have to take a break from this until I can actually program working code. However if someone else is inspired enough to demonstrate how to write a simple interpreter, don't let my failures put a stop to it.

Unfortunately most of the reference material I have to hand was on compiler design, and whilst its granted there are some shared techniques, I didn't have much information on interpreter design at all. (Other than some notes on the original BBC BASIC 2 in a book called the "Advanced BASIC ROM Guide" or simmilar title.

Somewhere on the Yahoo group you were also kind enough to post an example compiler a few years ago. It proved informative.

It should be possible "in theory" to implement other interpreters using BB4W as a host....  hmmm
(The thoughts of others on this in a new thread appreciated..)  When I can figure this out the hope was to slowly reimplement something like the Turtle Logo program I had mentioned elsewhere in a manner that's somewhat more readable, and cleaner for a modern host like BB4W or (BBC Basic For SDL).

I'm sadly finding as you've said on numerous previous instances, that most of the errors are entirely down to my own incompetence.

Richard Russell
 

On Fri, Sep 29, 2017 at 03:02 pm, <alex.farlie@...> wrote:
I'm trying to implement a lookup table... 
So you're looking for an efficient implementation of a key-value pair database?  That's a very common requirement (some programming languages have it built in) so on the basis of not reinventing the wheel I would suggest seeing what Google has to suggest.  You'll find several links to places like StackOverflow, for example this which came top in the results here.  Any suggestion for how to do it in plain C is likely to be directly applicable to BBC BASIC.

I presume you know how BBC BASIC does its variable lookup?  It uses 54 linked-lists, one for each possible initial character (A-Z, a-z, _ and `).  Having identified the correct list from the initial letter it does a linear search through the list until it finds a match (or not).  Obviously that can be quite slow, but the trick that BB4W and BBCSDL adopt is that having found the matching entry it is moved to the beginning of the list (being a linked-list that is very fast).  Because variables are commonly accessed multiple times in quick succession (perhaps in a loop) this ensures that subsequent lookups should be fast because it will be found at or near the start of the list.

> It should be possible "in theory" to implement other interpreters using BB4W as a host.... 

Probably not quite what you had in mind, but of course 'LB Booster' (LBB) - my Liberty BASIC clone - works by translating the LB source code to BBC BASIC source code.  When there is no direct equivalent in BBC BASIC the functionality is emulated (in BASIC) and the translator calls that routine at run-time.  LBB has an option to show the Liberty BASIC code in the left pane and the BBC BASIC translation in the right pane, and it can be instructive to compare the two.

Richard.

J.G.Harston
 

alex.farlie wrote:
160 (start%)$8="":(start%)$8=A$:PRINT A$
There's no such thing as string indirection with offset. The only binary indirection operators are ! and ?. Rewrite as:

160 $(start%+8)="":$(start%+8)=A$:PRINT A$
Think it through. How would the interpreter work out the difference between

fred$6 where fred is a variable
and
fred$6 where fred$ is a variable ?

--
J.G.Harston - jgh@... - mdfs.net/jgh

J.G.Harston
 

alex.farlie wrote:
220 link%=P%+(8+LEN(A$)+1)
230 IF link% MOD 4<>0 THEN : REM Align on 32bit word boundaries.
If you want to specify a multi-line IF then there must be nothing on the line after the THEN. What you have specified there is a single-line IF.

240 align%=link% MOD 4
250 link%=link%+(4-align%)
260 ENDIF
The easiest way to align an address is address=address AND -alignment, eg addr=addr AND -4 to align to a 4-byte address.

--
J.G.Harston - jgh@... - mdfs.net/jgh

J.G.Harston
 

Ok, let's start by giving those variables some actual names.

DIM table% 256
table_end%=table%+256
pointer%=table%
table%!0=-1:table%!4=-1 :REM Empty table

Now, it's conventional to use zero for "no data" for things like pointers and addresses as then you can easily do things like: IF value THEN this value is valid, instead of IF value<>-1 THEN ...

REPEAT
READ command$,fn_name$
eod%=(command$="*")
IF NOT eod% THEN PROCregister(command$,fn_name$,pointer%)
UNTIL eod%
PRINT "No more entries"
END
:
DEFPROCregister(command$,fn_name$, RETURN pointer%)
LOCAL align%,link%
PRINT command$,fn_name$,~pointer%
IF !pointer%<>-1 THEN ERROR 64,"Bad pointer supplied to registration function"

Your code suddenly changed to using P% without setting P% to anything.

IF pointer%+8+LENcommand$+1 > table_end% THEN ERROR 65,"Table full"

Having an end-of-table variable presumes you want to be checking for the end of the table.

link%=pointer%+8+LENcommand$+1
link%=(link%+1) AND -4 :REM Pad to 4-byte address
pointer%!0=link% :REM +0=link
pointer%!4=EVAL("^FN"+fn_name$+"()") :REM +4=address of function
$(pointer%+8)=command$ :REM +8=command string
pointer%=link% :REM Point to next entry
pointer%!0=-1 :REM Store 'end of data'
pointer%!4=-1
ENDPROC
:
DEF FNhi(A%):="hi"
DEF FNbye(A%):="bye"
:
DATA "HI","hi"
DATA "BYE","bye"
DATA "*",""

J.G.Harston
 

J.G.Harston wrote:
link%=(link%+1) AND -4 :REM Pad to 4-byte address
Sorry, only just woken up after a night shift. That should be:
link%=(link%+3) AND -4 :REM Pad to 4-byte address

In general, to align to an address use:
address = address AND -alignment

to pad to an alignment use:
address = (address+alignment-1) AND -alignment

Examples:
addr addr AND -4 (addr+3)AND-4
0 0 0
1 0 4
2 0 4
3 0 4
4 4 4
5 4 8
6 4 8

(glod knows if that formatting will get through. Display in monospaced characters to try and get it right)

--
J.G.Harston - jgh@... - mdfs.net/jgh

Alex Farlie
 

Thank you for the suggestions, but I am wondering if using proper structures would be better for type checking. (Makes comments to self, about remembering that BBC BASIC pointer syntax is more like C than Modula-2)


On 30/09/2017 15:58, J.G.Harston wrote:
Ok, let's start by giving those variables some actual names.

      DIM table% 256
      table_end%=table%+256
      pointer%=table%
      table%!0=-1:table%!4=-1              :REM Empty table

Now, it's conventional to use zero for "no data" for things like pointers and addresses as then you can easily do things like: IF value THEN this value is valid, instead of IF value<>-1 THEN ...
Duly noted, but I wanted to have something that was very distinctly "null", vs 0 meaning valid but nominally uninitialised.   Would it be better to define some kind of FNnull(P%), that can be used to check for something that's clearly invalid based on testable criteria?

IF pointer%+8+LENcommand$+1 > table_end% THEN ERROR 65,"Table full"

Having an end-of-table variable presumes you want to be checking for the end of the table.
It wasn't testing for end of table as such but testing for over-run, as your code indicates.

I am posting my revised thinking below (which is of course probably still broken,  and I'm inclined to think it would be better to throw it out and, start again when I can actually program :(

And before you ask yes I was looking into ways of how  the TurtleLogo program mentioned on another forum could be better implemented in BB4W.   Given that I'm already using BB4W features anyway, using linked structures might be a better option.... Naturally BB4W doesn't have associative arrays (i.e key,value(s) based arrays like more advanced languages.)

I somehow at this point doubt I'll ever be able to write a "useful" interpreter though, as I don't know enough.

Having got a command table, the next thing to figure out would be how to parse an input line into a series of commands which can be looked up in the table...  and then there is writing the command functions themselves, which would need the simplistic table to be extended to record which commands need what type of parameters.   At which point my brain melts in regard to the number of lookup tables needed to even do a simple  FD 10: LT 90 ; REPEAT 4 [ FD 10 LT 90 ] ...  Well it seemed like a nice idea when I started looking at it.

I think this needs a new thread...

Alex Farlie

Very simplistic (and certainly broken) lookup table code <<<<

   10 DIM table% 256
   20 table_end%=table%+256
   50 pointer%=table%
      table%!0=-1:table%!4=-1 :REM Empty table
   60 PROCinitalise  : REM Currently a dummy call but needed so function pointer are possible.

   70 eod%=FALSE
   80 REPEAT
   90   command$="":fn_name$=""
  100   READ command$,fn_name$
  110   IF command$="*" THEN
  120     eod%=TRUE
  130     PRINT "No more entires!"
  140     EXIT REPEAT
  150   ENDIF
  160   PROCregister(command$,fn_name$,pointer%)
  170 UNTIL eod%

      REM INPUT">"cmd$:tail$=""
      REM cmd%=FNfind_value(cmd$,table%)
      REM
      REM IF=cmd%=0 THEN
      REM ERROR 66, "I don't know : "+cmd$
      REM ELSE
      REM PRINT FN(cmd%)(tail$)
      REM ENDIF
  180 END
  190 :

      REM Register a command function, Non-aligned version.

  200 DEFPROCregister(command$,fn_name$, RETURN pointer%)
  210 LOCAL align%,link%
  220 PRINT ~pointer%, command$, fn_name$,
  230 IF !pointer%<>-1 THEN
  240   ERROR 64,"Bad pointer supplied to registeration function.!":END
  250 ELSE
        IF pointer%+8+LENcommand$+1 > table_end% THEN
          ERROR 65,"Table full"
        ELSE
  260     link%=pointer%+8+LEN(command$)+1
  270     pointer%!0=link%:PRINT ~pointer%!0
  280     pointer%!4=EVAL("^FN"+fn_name$+"()")
  290     $(pointer%+8)=command$
  300     pointer%=link%
  310     pointer%!0=-1
  320     pointer%!4=-1
  330   ENDIF
        ENDPROC

  340   : REM Match a cmd in a given table, and return the relevant function pointer.


  350   DEFFNfind_value(cmd$,dictionary_pointer%)
        REM cmd$ string contains a one word command.
  360   REM should be some check to make sure dictionary_pointer% is in fact pointing to a valid dictionary table
  370   LOCAL B$,p%,matched%,fn_link%
  380   p%=dictionary_pointer%:matched%=FALSE:fn_link%=0
  400   WHILE (NOT matched%) OR (NOT dictionary_end%)
  410     IF p%>table_end% OR !p%=-1 THEN
  420       REM reached end of data
  430       matched%=FALSE : dictionary_end%=TRUE
  440     ELSE
  450       B$=$(p%+8):
  460       IF cmd$=B$ THEN
  470         REM matched
  480         matched%=TRUE: fn_link%=p%!8
  490       ELSE
  500         matched%=FALSE: p%=!p% : REM follow the link to the nominal next entry and try again.
  510       ENDIF
  520     ENDIF
  530   ENDWHILE
  550   IF matched% THEN
  560     =fn_link%
  570   ELSE
  580     =0
  590   ENDIF
  600   :
  610   DEF PROCinitalise:
  620   REM initalise dictionary
  630   REM table%!0=-1
  640   REM table%!4=-1
  650   ENDPROC
  660   :
  670   DEF FNhi(RETURN tail$):="hi"
  680   DEF FNbye(RETURN tail$):="bye"
  690   :
  700   DATA "HI","hi"
  710   DATA "BYE","bye"
  720   DATA "*",""

Previous Topic Next Topic