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
NewOPL
-
- Posts: 318
- Joined: Tue Jan 03, 2023 7:54 pm
Re: NewOPL
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.
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.
-
- Posts: 318
- Joined: Tue Jan 03, 2023 7:54 pm
Re: NewOPL
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:
I've got this parsing a few simple OPL lines:
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.
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>
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)
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.
-
- Posts: 318
- Joined: Tue Jan 03, 2023 7:54 pm
Re: NewOPL
I've updated the parser to the stage where it can parse the game.opl file from the LZ programming manual:
If you run:
you get a huge amount of debug output and then some summary lines:
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.
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
Code: Select all
67 lines scanned Ok
0 lines scanned failed
0 lines blank
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.
-
- Posts: 318
- Joined: Tue Jan 03, 2023 7:54 pm
Re: NewOPL
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):
when run through the translator like this:
Produces this intermediate code (intcode.txt file):
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:
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.
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
Code: Select all
./newopl_tran testa.opl
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:()
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)
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.