Seite 1 von 1

BASIC code parsen...

Verfasst: Dienstag 19. August 2014, 17:41
von jens
Ich überlege gerade ein wenig, wie man BASIC Code parsen kann. Einen schrecklichen parser hatte ich gebastelt, dann gemerkt das es evtl. komplett mit einer Regular-Expression geht. Denn so kompliziert ist BASIC ja nicht.

So ganz klappt es aber doch noch nicht:

Code: Alles auswählen

ascii_listing = """\
10 PRINT "A STRING"
20 ' ONLY A COMMENT
30 A$="ANOTHER STRING" ' INLINE COMMENT
40 REM COMMENT AND NEXT LINE IS CODE ONLY:
50 B=3
60 PRINT "STRING WIHOUT CLOSE!
70 GOTO 10 : REM COMMENT AS SECOND STATEMENT
"""

regex = re.compile(
    r"""
        ^
        (?P<line_number>\d+)\s?
        (
                (
                    (?P<pre_comment>[^"\n]*)
                    (?P<comment> ('|REM).* )
                )?
            |
                (
                    (?P<pre_string>[^"\n]*)
                    (?P<string>"[^"\n]*"?)
                    (?P<post_string>[^"\n]*)
                )?
            |
            (?P<code>.*)
        )$
    """,
    re.VERBOSE | re.MULTILINE
)


def debug_match(match):
    print "Line: >>>%s<<<\n" % match.group(0)
    match_groupdict = match.groupdict()
    for name, text in match_groupdict.items():
        if text is not None:
            print "%15s: %r" % (name, text)
    print "-" * 79

print "-" * 79
regex.sub(debug_match, ascii_listing)
Ausgabe:

Code: Alles auswählen

-------------------------------------------------------------------------------
Line: >>>10 PRINT "A STRING"<<<

    line_number: '10'
     pre_string: 'PRINT '
         string: '"A STRING"'
    post_string: ''
-------------------------------------------------------------------------------
Line: >>>20 ' ONLY A COMMENT<<<

        comment: "' ONLY A COMMENT"
    line_number: '20'
    pre_comment: ''
-------------------------------------------------------------------------------
Line: >>>30 A$="ANOTHER STRING" ' INLINE COMMENT<<<

    line_number: '30'
     pre_string: 'A$='
         string: '"ANOTHER STRING"'
    post_string: " ' INLINE COMMENT"
-------------------------------------------------------------------------------
Line: >>>40 REM COMMENT AND NEXT LINE IS CODE ONLY:<<<

        comment: 'REM COMMENT AND NEXT LINE IS CODE ONLY:'
    line_number: '40'
    pre_comment: ''
-------------------------------------------------------------------------------
Line: >>>50 B=3<<<

    line_number: '50'
           code: 'B=3'
-------------------------------------------------------------------------------
Line: >>>60 PRINT "STRING WIHOUT CLOSE!<<<

    line_number: '60'
     pre_string: 'PRINT '
         string: '"STRING WIHOUT CLOSE!'
    post_string: ''
-------------------------------------------------------------------------------
Line: >>>70 GOTO 10 : REM COMMENT AS SECOND STATEMENT<<<

        comment: 'REM COMMENT AS SECOND STATEMENT'
    line_number: '70'
    pre_comment: 'GOTO 10 : '
-------------------------------------------------------------------------------

Damit kann man eigentlich schon gut leben, aber so ganz richtig ist es nicht, denn z.B. ist Zeile 30 falsch: post_string: " ' INLINE COMMENT" sollte eigentlich als "comment" erkannt werden.

Geht das überhaupt, alles mit einer RE zu erschlagen?

Re: BASIC code parsen...

Verfasst: Dienstag 19. August 2014, 20:00
von BlackJack
@jens: Das sieht alles etwas zu kurz gegriffen aus. Du hast da entweder eine Zeile mit irgendwas gefolgt von einem Kommentar, oder irgendwas, Zeichenkette, irgendwas. Was ist denn mit Zeilen mit Zeichenkette *und* Kommentar, oder mit Zeilen mit mehr als einer Zeichenkette? ``10 INPUT"NAME";N$;PRINT"HALLO ";N$;"!":REM YEAH``.

Und aus eigener Erfahrung mit einem Tokenizer für CBM BASIC: Neben ``REM`` fällt auch ``DATA`` ein wenig aus dem Rahmen. Ist vermutlich bei Deinem BASIC auch so.

Re: BASIC code parsen...

Verfasst: Dienstag 19. August 2014, 20:30
von jens
Stimmt. Nach DATA kann wohl "alles" kommen.

Bin gerade dabei mehrere REGEXs zu bauen und diese nacheinander ab zu arbeiten. So elegant wirkt das allerdings nicht.

Re: BASIC code parsen...

Verfasst: Dienstag 19. August 2014, 20:48
von BlackJack
@jens: Naja, ”alles”, aber nur bis zum nächsten ':' der nicht in einer Zeichenkette steckt.

Der Code ist mir zwar ein bisschen peinlich und, äh, unterdokumentiert, aber das hier benutze ich zum (de)tokenisieren von CBM BASIC V2: https://bitbucket.org/blackjack/pycbm-b ... at=default

Re: BASIC code parsen...

Verfasst: Dienstag 19. August 2014, 20:54
von jens
Hab mal ein bisschen mit DragonPy probiert...

Offensichtlich darf nach DATA alles kommen. Egal was, es wird mit einem Komma getrennt und an READ weiter gereicht.

bsp:

Code: Alles auswählen

10 PRINT "GO":DATA 00
20 READ HB$
30 IF HB$="END" THEN 130
40 PRINT HB$;
50 GOTO 20
60 ' FOO
70 DATA XX,DATA YY
80 DATA "JO"
90 DATA 4F,'REM?
100 PRINT "X":DATA 5B
110 ' BAR
120 DATA 5F,END
130 PRINT:PRINT "BYE"
Ausgabe ist dann:
RUN
GO
00XXDATA YYJO4F'REM?5B5F
BYE
OK

EIN ":" in DATA geht aber anscheinend nicht. bzw. wird ignoriert.

Re: BASIC code parsen...

Verfasst: Dienstag 19. August 2014, 21:03
von BlackJack
Also beim C64 kann man nach einem ``DATA`` wieder ganz normal mit der nächsten Anweisung weitermachen wenn man die mit einem Doppelpunkt trennt:

Code: Alles auswählen

10 DATA A,B,C:PRINT"HALLO"
RUN
HALLO

READY.

Re: BASIC code parsen...

Verfasst: Dienstag 19. August 2014, 22:00
von jens
Stimmt, das geht auch:

Code: Alles auswählen

10 DATA A,B,C:PRINT"HALLO"
20 FOR I=1 TO 3
25 READ D$
30 PRINT I;D$
40 NEXT
RUN
HALLO
1 A
2 B
3 C
OK
Dann muß ich wohl vorher was falsch gemacht haben...

Re: BASIC code parsen...

Verfasst: Dienstag 19. August 2014, 22:08
von jens
BlackJack hat geschrieben:Der Code ist mir zwar ein bisschen peinlich und, äh, unterdokumentiert, aber das hier benutze ich zum (de)tokenisieren von CBM BASIC V2: https://bitbucket.org/blackjack/pycbm-b ... at=default
So viel wie du da gemacht hast, will ich erstmal nicht.
Ich möchte nur Unterscheiden zwischen: Code, String, Data und Kommentare
Alles weitere kommt im nächsten Schritt.

Dennoch finde ich deine def _tokenize() mit dem regex.finditer() interessant.
Ich experimentiere, ob ich nicht mit der regex ("|DATA|:|REM|') und dem selben Prinzip klar komme: Wäre allerdings dann so eine "state" Geschichte. Also:
1. Beim ersten " bin ich im String-Teil, bis wieder ein " oder \n kommt
2. Beim ersten DATA warten bis : oder \n
3. erstes REM oder ' bis \n -> Kommentar
4. alles andere ist Code

Re: BASIC code parsen...

Verfasst: Dienstag 19. August 2014, 22:42
von BlackJack
@jens: Bei 2. darauf achten das der ':' auch innerhalb einer Zeichenkette stehen kann und dann nicht ”zählt”. Beispiel ``10 DATA"ANSWER:",42:PRINT"HALLO"``, da muss man bis zum ':' vor dem ``PRINT`` gehen.

Re: BASIC code parsen...

Verfasst: Mittwoch 20. August 2014, 10:40
von jens
Danke für den Hinweis. Hätte ich sicherlich übersehen...

Mit https://github.com/jedie/DragonPy/commi ... 51af4ef708 ist nun die erste Funktionierende Lösung fertig.
Hoffe ich hab mit den unittests auch alle Eventualitäten abgedeckt.

So richtig schön finde ich die Lösung nicht. Es wiederholen sich recht ähnliche Code-Teile, die aber doch kleine Unterschiede haben :?