Home-Produkte-Testarea-Kontakt-Datenschutz-Aktualisiert: 03-May-2009
< Voriger Tag   Nächster Tag >

Sonntag, 03. Mai 2009

Java: Compilerbau mit ANTLR 3 und StringTemplate (4)

Mit 'print' integer; wird der Compiler nun um die Ausgabe von Ganzzahlen per print erweitert.

print123.prg

print 123;
helloworld;

Lexer: INTEGER

Erster Schritt ist die Erweiterung des Lexers, um die Erkennung von Ganzzahlen. Dies geschieht mit

INTEGER : ('0'..'9')+ ;

('0'..'9')+ bedeutet, dass die Ziffern 0, 1, 2, ... 9 ein, oder beliebig oft hintereinanstehen dürfen und dem Tokennamen INTEGER zugeordnet werden.

Lexersymbole wie INTEGER müssen in ANTLR mit einen Großbuchstaben beginnen, die Namen der Regeln wie statement mit einen Kleinbuchstaben. Es ist in ANTLR üblich die Lexersymbole durchgehen groß zu schreiben und für die Regeln die lowerCamelCase-Schreibweise zu benutzen.

Grammatik: 'print' integer;

In der Grammatik wird $INTEGER.text in das const-StringTemplate (Konstante) umgesetzt, das an den print-Befehl weitergereicht wird.

Dieser setzt sich aus den direkt angebenen 'print'-Token und dem Ergebnis der integer-Regel zusammen.

print
    : 'print' integer -> print(value={$integer.st})
    ;

integer
    : INTEGER -> const(value={$INTEGER.text})
    ;

Grammar.g

grammar Grammar;

options {output=template;}


program
    scope {
        /* Statement list */
        List statements;
    }

    @init {
        /* Init statement list */
        $program::statements = new ArrayList();
    }

    : statement* EOF -> program(statements={$program::statements})
    ;

statement
    : helloworld ';' {$program::statements.add($helloworld.st);}
    | print      ';' {$program::statements.add($print.st);}
    ;

helloworld
    : 'helloworld' -> helloWorld()
    ;

print
    : 'print' integer -> print(value={$integer.st})
    ;

integer
    : INTEGER -> const(value={$INTEGER.text})
    ;


// Lexer

INTEGER : ('0'..'9')+ ;
WS      : (' ' | '\t' | '\r' | '\n')+ {$channel=HIDDEN;} ;

Templates: const() und print()

Die Templates werden um const() für die Ganzzahl und print() zur Generierung des Quelltextes für die Zahlenausgabe erweitert.

Java.stg

group Java;

program(statements) ::= <<
public class HelloWorld {
    public static void main(String[] args) {
        <statements; separator="\n">
    }
}
>>


helloWorld() ::= "System.out.println(\"Hello World!\");"


const(value) ::= "<value>"
print(value) ::= "System.out.println(<value>);"

C.stg

group C;

program(statements) ::= <<
#include \<stdio.h\>

int main(int argc, char *argv[]) {
    <statements; separator="\n">

    return 0;
}
>>


helloWorld() ::= <<
printf("Hello World!\\n");
>>


const(value) ::= "<value>"

print(value) ::= <<
printf("%d\\n", <value>);
>>

PHP.stg

group PHP;

program(statements) ::= <<
<statements; separator="\n">
>>


helloWorld() ::= <<
echo "Hello World!\\n";
>>


const(value) ::= "<value>"

print(value) ::= <<
echo <value>, "\n";
>>

Main

Das Hauptprogramm bleibt unverändert.

Main.java

import java.io.*;
import org.antlr.runtime.*;
import org.antlr.stringtemplate.*;
import org.antlr.stringtemplate.language.*;


public class Main {
    public static void main(String[] args)  {
        StringTemplateGroup templates;

        String templateFilename = "";
        String programFilename  = "";


        if(args.length < 2) {
            System.out.println("Main [template] [program]");
            System.exit(0);
        } else {
            templateFilename = args[0];
            programFilename  = args[1];
        }

        try {
            templates = new StringTemplateGroup(new FileReader(templateFilename),
                                                AngleBracketTemplateLexer.class);

            CharStream        input  = new ANTLRFileStream(programFilename);
            GrammarLexer      lexer  = new GrammarLexer(input);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            GrammarParser     parser = new GrammarParser(tokens);

            parser.setTemplateLib(templates);
            RuleReturnScope r = parser.program();

            System.out.println(r.getTemplate().toString());

        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

Compilieren und ausführen

Und wird wie in Hello World! beschrieben compiliert und ausgeführt.

> java Main Java.stg print123.prg

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println(123);
        System.out.println("Hello World!");
    }
}


> java Main PHP.stg print123.prg

echo 123, "\n";
echo "Hello World!\n";

Bekannte Fehler: Zu große Ganzzahlen möglich

Was in diesem Beispiel fehlt ist die Erkennung von zu großen Zahlen. Da INTEGER aus beliebig vielen Ziffern bestehen darf, kann die gängige, maximale Größe von 4 294 967 295 für Ganzzahlen schnell überschritten werden, ohne dass dies der Compiler erkennt: print 9876543210. Hierfür muss später noch eine Fehlererkennung eingebaut werden.

[Direktlink]

< Voriger Tag   Nächster Tag >

  RSS V0.91

<Mai 2009 >
    010203
04050607080910
11121314151617
18192021222324
25262728293031

Home-Produkte-Testarea-Kontakt-Datenschutz-Aktualisiert: 03-May-2009
(C) 2000-2018 by Sven Drieling