NewOPL

Use this section to showcase your programming examples. Or to ask members for programming suggestions
User avatar
Martin
Global Admin
Posts: 306
Joined: Mon Jan 02, 2023 5:18 pm

NewOPL UDG

Post by Martin »

Wow big jump this.

(1) no commas in the bracketed version (but a space after the 90 )
(2) has NewOPL got more than 8 UDG's
(3) how many pixels across in a character (202 needs at least 8 as opposed to 5)
(4) I suppose there is some sort of backwards compatibility as OldOPL will always be less, but would a legacy UDG character have a gap to the left with 'blank' pixels?

Martin
amenjet
Posts: 318
Joined: Tue Jan 03, 2023 7:54 pm

Re: NewOPL

Post by amenjet »

Those are just numbers, so no validation checking, this is just to test the syntax. Andrew interesting point, though, I wonder where or if the validation checking is done?
I need to get arrays handled somehow, then fill in all the qcodes.

For now it's just strings in and parsing, there's no function behind it.
amenjet
Posts: 318
Joined: Tue Jan 03, 2023 7:54 pm

Re: NewOPL

Post by amenjet »

While attempting to add array data types, I found that a simple parser won't work as array indices are in brackets and those brackets can't be easily differentiated from expression precedence brackets. So, I am adding a recursive descent parser that parses the lines, then expressions are evaluated using the RPN scheme as before. The grammar for the parser is currently:

Code: Select all

<file>       : <procdef> <cline>*
<cline>      : <line> | <line> ':' <cine>+
<procdef>    : <string> ':'
<line>       : <assignment> | <command> |
               'LOCAL' <varlist> |
	       'GLOBAL' <varlist> |
               'IF' <expression> | 'ELSE' | 'ENDIF' |
	       'DO' | 'WHILE' <expression> |
	       'REPEAT' | 'UNTIL' <expression>
<string>     : '"'<textchar>*'"'
<number>     : <integer> | <float>
<atom>       : <string> | <number> | <variable>	       
<sub_expr>   : '(' <expression> ')'
<eitem>      : <operator> | <function> | <atom> | <sub_expr> 
<expression> : <eitem>+
<variable>   : <vname> | <vname>'%' | <vname>'$' |
               <vname>'(' <expression> ')' |
	       <vname>'%''(' <expression> ')' |
	       <vname>'$''(' <expression> ')' |
	       <vname>'$''(' <expression ',' <expression> ')'
<command>    : <command-list-name-entry> <expression>
<assignment> : <variable> '=' <expression>
<varlist>    : <variable>+
<vname>      : <A-Z><A-Z0-9>*
<operator>   : <operator-list-string>	      
I've got this parsing a few simple OPL lines:

Code: Select all

A= A + (2*3+3*Z%-   ABC%*1.1)
X%=2
X%=Y%
X%= Z%
A% = 1+2
A% = B%
A% = A% + B%
A% = 2 + (A% * B%)
A = A% + (2*3+3*Z%-   ABC%*1.1)
A% = 20

PRINT "abc"
PRINT "asdfgh"
PRINT   "    abc    "
PRINT A%
PRINT B
PRINT ABC
PRINT ABC%
PRINT A$
PRINT DEF$
PRINT A(2)
PRINT B%(10)
PRINT D$(2)
PRINT F$(10,12)
This parser is currently in a separate file called

testtok.c

I think this parser might be useful for the conditional keywords as well. It's a second way to parse those, at least.
amenjet
Posts: 318
Joined: Tue Jan 03, 2023 7:54 pm

Re: NewOPL

Post by amenjet »

I've updated the parser to the stage where it can parse the game.opl file from the LZ programming manual:

Code: Select all

game:
LOCAL e$(2)
LOCAL a%,b%,b1%,c%,c1%,x%,y%,i%,sc%
graphic: :CURSOR OFF
e$=CHR$(4)+CHR$(6)
b%=20 :c%=12 :x%=3 :y%=1
DO
 CLS :PRINT REPT$(CHR$(158),80)
 AT x%,y% :PRINT CHR$(7)
 a%=1 :c%=1+RND*4
 DO :c1%=1+RND*4 :UNTIL c%<>c1%
 DO
  AT a%,c% :PRINT CHR$(0)
  AT a%,c1% :PRINT CHR$(0) :BEEP b%,10*b%
  AT a%,c% :PRINT CHR$(1)
  AT a%,c1% :PRINT CHR$(1) :BEEP b%,10*b%
  AT a%,c% :PRINT CHR$(2)
  AT a%,c1% :PRINT CHR$(2) :BEEP b%,10*b%
  AT a%,c% :PRINT CHR$(3)
  AT a%,c1% :PRINT CHR$(3) :BEEP b%,10*b%
  AT a%,c% :PRINT e$
  AT a%,c1% :PRINT e$ :BEEP b%,10*b%
  AT a%,c% :PRINT CHR$(5)
  AT a%,c1% :PRINT CHR$(5) :BEEP b%,10*b%
  AT a%,c% :PRINT " " :AT a%,c1% :PRINT " "
  i%=KEY
  IF i%
   IF i%=%S AND y%>1
    AT x%,y% :PRINT CHR$(158)
    y%=y%-1 :AT x%,y% :PRINT CHR$(7)
   ENDIF
   IF i%=%X AND y%<4
    AT x%,y% :PRINT CHR$(158)
    y%=y%+1 :AT x%,y% :PRINT CHR$(7)
   ENDIF
  ENDIF
  a%=a%+1
  IF a%=x% AND (c%=y% OR c1%=y%) :REM Hit
   i%=0
   DO
    AT x%,y% :PRINT CHR$(170+i%)
    BEEP 10,100+i%
    i%=i%+1
    BEEP 10,100-i%
   UNTIL i%=30
   b%=b%+5 :a%=20 :x%=x%+2
   IF x%>20
    CLS : PRINT "GAME OVER"
    PRINT "SCORE:",sc% :PAUSE 40
    WHILE KEY :ENDWH :REM Drain buffer
    GET :RETURN
   ENDIF
  ENDIF
 UNTIL a%=20
 sc%=sc%+1
 IF b%>12
  b%=b%-2
 ELSEIF b%<6
  IF b1%
   b%=b%-1 :b1%=0
  ELSE
   b1%=1
  ENDIF
 ELSE
  b%=b%-1
 ENDIF
UNTIL 0

If you run:

Code: Select all

./testtok game.opl
you get a huge amount of debug output and then some summary lines:

Code: Select all

67 lines scanned Ok
 0 lines scanned failed
 0 lines blank
This shows 67 lines accepted and none rejected. Blank lines are also counted. This means that the parser found nothing it didn't understand in this case. There's definitely bugs in this and missing syntax. It also doesn't do any checking such as checking variable names aren't keywords and so on, that's next. Then this parser will be put in front of the RPN code generator and form the translator.

I thought someone out there might be interested in such a thing, and maybe want to try some other OPL.

I did find some mildly interesting things, for instance, you can use functions as commands, so:

GET

is a valid OPL line and is used a lot. What isn't used as much is:

COS(98+2)

which is also a valid OPL line. And fairly useless. But valid.

The parser is more permissive than it should be at the moment, but it shouldn't fail on any valid OPL, that's the important test with this version.
amenjet
Posts: 318
Joined: Tue Jan 03, 2023 7:54 pm

Re: NewOPL

Post by amenjet »

For anyone who is interested...

The newopl translator can now parse most of the Organiser OPL. This is a pure syntax operation and so no semantic checking is done. It generates an intermediate code that should be easily translatable into QCode.

As an example, this (the file testa.opl):

Code: Select all

testa:

local A, A%

DO

IF A% > 2.5

A = 12 A% = 13.6

IF A% =5
A% = 10.2 * A%
ENDIF

ELSE

A = 1 * 10
A% = (1*A%) + (10.2*A)

ENDIF

UNTIL A% = 10.2
when run through the translator like this:

Code: Select all

./newopl_tran testa.opl
Produces this intermediate code (intcode.txt file):

Code: Select all

( dump_exp_buffer) N1 EXP_BUFF_ID_VARIABLE             f rq:f 'LOCAL' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N2 EXP_BUFF_ID_VARIABLE             f rq:f 'A' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N3 EXP_BUFF_ID_VARIABLE             f rq:f 'A%' npar:0 nidx:0  0:  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_DO           L:1     i rq:i 'DO' npar:0 nidx:22515  0:  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_VARIABLE             i rq:i 'A%' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N4 EXP_BUFF_ID_AUTOCON              f rq:f 'autocon i->f' npar:0 nidx:48  2: 2 1  nb 0:()
( dump_exp_buffer) N2 EXP_BUFF_ID_FLT                  f rq:f '2.5' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N3 EXP_BUFF_ID_OPERATOR             f rq:f '>' npar:0 nidx:22515  2: 2 1  nb 0:()
( dump_exp_buffer) N5 EXP_BUFF_ID_IF           L:2     i rq:i 'IF' npar:0 nidx:22515  0:  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_VARIABLE             f rq:f 'A' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N2 EXP_BUFF_ID_INTEGER              i rq:i '12' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N4 EXP_BUFF_ID_AUTOCON              f rq:f 'autocon i->f' npar:0 nidx:0  2: 2 1  nb 0:()
( dump_exp_buffer) N3 EXP_BUFF_ID_OPERATOR             f rq:f ':=' npar:0 nidx:32766  2: 2 1  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_VARIABLE             i rq:i 'A%' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N2 EXP_BUFF_ID_FLT                  f rq:f '13.6' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N4 EXP_BUFF_ID_AUTOCON              i rq:i 'autocon f->i' npar:0 nidx:0  2: 2 1  nb 0:()
( dump_exp_buffer) N3 EXP_BUFF_ID_OPERATOR             i rq:i ':=' npar:0 nidx:32766  2: 2 1  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_VARIABLE             i rq:i 'A%' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N2 EXP_BUFF_ID_INTEGER              i rq:i '5' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N3 EXP_BUFF_ID_OPERATOR             i rq:i '=' npar:0 nidx:22515  2: 2 1  nb 0:()
( dump_exp_buffer) N4 EXP_BUFF_ID_IF           L:3     i rq:i 'IF' npar:0 nidx:22515  0:  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_VARIABLE             i rq:i 'A%' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N2 EXP_BUFF_ID_FLT                  f rq:f '10.2' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N3 EXP_BUFF_ID_VARIABLE             f rq:f 'A%' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N4 EXP_BUFF_ID_OPERATOR             f rq:f '*' npar:0 nidx:22515  2: 3 2  nb 0:()
( dump_exp_buffer) N6 EXP_BUFF_ID_AUTOCON              i rq:i 'autocon f->i' npar:0 nidx:0  2: 4 1  nb 0:()
( dump_exp_buffer) N5 EXP_BUFF_ID_OPERATOR             i rq:i ':=' npar:0 nidx:32766  2: 4 1  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_ENDIF        L:3     U rq:U 'ENDIF' npar:0 nidx:22515  0:  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_ELSE         L:2     i rq:i 'ELSE' npar:0 nidx:22515  0:  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_VARIABLE             f rq:f 'A' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N2 EXP_BUFF_ID_INTEGER              i rq:i '1' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N3 EXP_BUFF_ID_INTEGER              i rq:i '10' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N4 EXP_BUFF_ID_OPERATOR             i rq:i '*' npar:0 nidx:22515  2: 3 2  nb 0:()
( dump_exp_buffer) N6 EXP_BUFF_ID_AUTOCON              f rq:f 'autocon i->f' npar:0 nidx:0  2: 4 1  nb 0:()
( dump_exp_buffer) N5 EXP_BUFF_ID_OPERATOR             f rq:f ':=' npar:0 nidx:32766  2: 4 1  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_VARIABLE             i rq:i 'A%' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N2 EXP_BUFF_ID_SUB_START            U rq:i '' npar:0 nidx:48  0:  nb 0:()
( dump_exp_buffer) N3 EXP_BUFF_ID_INTEGER              i rq:i '1' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N4 EXP_BUFF_ID_VARIABLE             i rq:i 'A%' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N5 EXP_BUFF_ID_OPERATOR             i rq:i '*' npar:0 nidx:22515  2: 4 3  nb 0:()
( dump_exp_buffer) N15 EXP_BUFF_ID_AUTOCON              f rq:f 'autocon i->f' npar:0 nidx:0  2: 11 5  nb 0:()
( dump_exp_buffer) N6 EXP_BUFF_ID_SUB_END              U rq:i '' npar:0 nidx:32766  0:  nb 0:()
( dump_exp_buffer) N7 EXP_BUFF_ID_SUB_END              U rq:i '' npar:0 nidx:32766  0:  nb 0:()
( dump_exp_buffer) N8 EXP_BUFF_ID_SUB_START            U rq:i '' npar:0 nidx:29822  0:  nb 0:()
( dump_exp_buffer) N9 EXP_BUFF_ID_FLT                  f rq:f '10.2' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N10 EXP_BUFF_ID_VARIABLE             f rq:f 'A' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N11 EXP_BUFF_ID_OPERATOR             f rq:f '*' npar:0 nidx:22515  2: 10 9  nb 0:()
( dump_exp_buffer) N12 EXP_BUFF_ID_SUB_END              U rq:i '' npar:0 nidx:32766  0:  nb 0:()
( dump_exp_buffer) N13 EXP_BUFF_ID_SUB_END              U rq:i '' npar:0 nidx:32766  0:  nb 0:()
( dump_exp_buffer) N14 EXP_BUFF_ID_OPERATOR             f rq:f '+' npar:0 nidx:22515  2: 11 5  nb 0:()
( dump_exp_buffer) N17 EXP_BUFF_ID_AUTOCON              i rq:i 'autocon f->i' npar:0 nidx:0  2: 14 1  nb 0:()
( dump_exp_buffer) N16 EXP_BUFF_ID_OPERATOR             i rq:i ':=' npar:0 nidx:32766  2: 14 1  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_ENDIF        L:2     U rq:U 'ENDIF' npar:0 nidx:22515  0:  nb 0:()

( dump_exp_buffer) N1 EXP_BUFF_ID_VARIABLE             i rq:i 'A%' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N2 EXP_BUFF_ID_FLT                  f rq:f '10.2' npar:0 nidx:0  0:  nb 0:()
( dump_exp_buffer) N4 EXP_BUFF_ID_AUTOCON              i rq:i 'autocon f->i' npar:0 nidx:48  2: 2 1  nb 0:()
( dump_exp_buffer) N3 EXP_BUFF_ID_OPERATOR             i rq:i '=' npar:0 nidx:22515  2: 2 1  nb 0:()
( dump_exp_buffer) N5 EXP_BUFF_ID_UNTIL        L:1     i rq:i 'UNTIL' npar:0 nidx:22515  0:  nb 0:()
This has a lot of debug information, but you should be able to pick out the original OPL, translated into codes similar to QCode.
The L:n are the conditional (IF,DO,WHILE) construct levels, and tie the ENDIF to an IF etc. They should allow the branch instructions to be generated.
The 'autocon' lines are the auto type conversions which are needed to get ints and foats to work together. (There is a bug in this listing...)

There are some other files, translate.opl looks like this:

Code: Select all

A%

DO
IF (A% > 2.5)
A := 12
A% := 13.6
IF (A% = 5)
A% := (10.2 * A%)
ENDIF
ELSE
A := (1 * 10)
A% := ((1 * A%) + (10.2 * A))
ENDIF
UNTIL (A% = 10.2)
And is the intermediate code fed into a sort of REVTRAN to get back to OPL. It should match the original OPL file.

output.txt is all the debug from the program when it runs and can be enormous, but allows the internal work to be examined when it goes wrong.
There are some remaining parse problems, I've put them into issues on the githib page.

You should be able to feed any OPL into the program and see what it comes up with. If you spot anything wrong, I'd like to know.
Post Reply