Page 1 of 1

Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Mon Mar 16, 2009 6:30 pm
by cyberpython
Μιας και είμαι άρρωστος σήμερα και δεν έχω τι να κάνω ας δούμε πως μπορούμε να κατασκευάσουμε ένα μικρό lexer με το SableCC 3.2 και πως μπορούμε να τον χρησιμοποιήσουμε.

Τί είναι το SableCC;

Το SableCC είναι ένα πρόγραμμα που παίρνει ως είσοδο ένα σύνολο από κανόνες που περιγράφουν μία γλώσσα και παράγει τις απαραίτητες τάξεις για τη λεκτική και συντακτική ανάλυση εκφράσεων της γλώσσας αλλά και το μετασχηματισμό του συντακτικού δέντρου σε αφηρημένο και τη διάσχιση του δέντρου αυτού.

Το SableCC 3.2 είναι διαθέσιμο εδώ: http://sablecc.org/wiki/DownloadPage

Εμείς θα περιγράψουμε πως μπορούμε να κατασκευάσουμε ένα λεκτικό αναλυτή (lexer) για ένα μικρό υποσύνολο της QBasic που θα παίρνει ως είσοδο ένα πρόγραμμα γραμμένο σε αυτό το υποσύνολο της QBasic και στη συνέχεια θα το "σπάει" στα κομμάτια (tokens) από τα οποία αποτελείται και θα προσπαθεί να αναγνωρίσει κάθε κομμάτι. Έτσι στο τέλος θα έχουμε ένα σύνολο από tokens που μπορούμε να τα κάνουμε ότι θέλουμε - εμείς θα παράγουμε ένα έγγραφο html μορφοποιημένο κατάλληλα (π.χ. έντονα keywords) - σημαντικό είναι οτι σε αυτή την περίπτωση μας χρειάζονται και τα κενά, τα tabs και οι αλλαγές γραμμής(για να έχει την ίδια μορφή και στοίχιση και το τελικό αρχείο html).

Η γλώσσα που θα αναγνωρίζει ο lexer μας θα είναι αυτή:

Δεσμευμένες Λέξεις (token: keyword)
PRINT
CLS
DO
WHILE
LOOP
INPUT
IF
THEN
ELSE
END
SELECT
CASE
OR
FOR
TO
NEXT
STEP
CONST
(εδώ δε χρειαζόμαστε διαφορετικό token για κάθε δεσμευμένη λέξη - π.χ. για συντακτική ανάλυση - αφού απλά θέλουμε οι δεσμευμένες λέξεις να έχουν το ίδιο στυλ)


Μεταβλητές (για αλφαριθμητικά, token: string_var)
γράμμα ακολουθούμενο από γράμματα ή ψηφία ή κάτω παύλες και στο τέλος $
π.χ. alphanum_1$


Μεταβλητές (για ακεραίους, token: num_var)
γράμμα ακολουθούμενο από γράμματα ή ψηφία ή κάτω παύλες
π.χ. var_001


Αλφαριθμητικές σταθερές (token: string_const)
οποιοαδήποτε ακολουθία χαρακτήρων ανάμεσα σε διπλά εισαγωγικά (quotes)
π.χ. "This is - a - test string. :)"


Αριθμητικές σταθερές (token: num_const)
οποιοαδήποτε ακολουθία ενός ή περισσότερων ψηφίων
π.χ. 0 12 213 κλπ.


Σύμβολα (token: symbol)
+-*/(),=;

Για τα σύμβολα '<' και '>' πρέπει να ορίσουμε ειδικά tokens αφού θα απαιτούν ξεχωριστό χειρισμό (δεν επιτρέπονται στην html οι αντίστοιχοι χαρακτήρες) :

lt
<

gt
>

Σχόλια (token: comment)
Μονό εισαγωγικό (') ακολουθούμενο από οποιοδήποτε χαρακτήρα έως την αλλαγή γραμμής

Επίσης, θα χρειαστούμε και τα κενά και tabs για διατήρηση της στοίχισης του κώδικα (ενώ αν ο lexer προοριζόταν για χρήση από έναν συντακτικό αναλυτή τότε θα έπρεπε να αγνοούνται). Ακόμη, τα κενά θα παράγουν διαφορετικό token από τα tabs αφού θέλουμε να τα χειριστούμε διαφορετικά στη συνέχεια (για κάθε tab θα παράγουμε 8 &nbsp; στην html):

Κενό (token: space)
' '

Tab (token: tab)
ο χαρακτήρας με ascii-code 9

Ομοίως πρέπει να χειριστούμε και τις αλλαγές γραμμής:

Αλλαγή γραμμής (token: newline)
cr, lf ή cr lf


Ορισμός των Helpers
Τώρα πρέπει με κάποιο τρόπο να εκφράσουμε τα παραπάνω με τρόπο που να μπορεί να τα καταλάβει το SableCC.
Αρχικά δημιουργούμε ένα νέο κενό αρχείο απλού κειμένου και γράφουμε τα εξής:

Code: Select all

Package subbasic;

Helpers

Tokens

Η γραμμή

Code: Select all

Package subbasic;

θα χρησιμοποιηθεί από το SableCC για τη δημιουργία ενός φακέλου με το όνομα subbasic όπου θα τοποθετηθούν όλα τα αρχεία που θα παράγει.

Κάτω από το Helpers μπορούμε να ορίσουμε σύμβολα που θα μας χρειαστούν για τον ορισμό των tokens.
Π.χ. για να ορίσουμε τις αριθμητικές σταθερές πρέπει πρώτα να έχουμε ορίσει το ψηφίο (δε θέλουμε όμως να αναγνωρίζεται ως token γι' αυτό το ορίζουμε ως helper ώστε να μην υπάρχει στην έξοδο που παράγει ο λεκτικός αναλυτής). Έτσι, ορίζουμε τα ψηφία στο τμήμα helpers ως εξής:

Code: Select all

digit 			= ['0'..'9'];
Για να ορίσουμε ένα helper γράφουμε το όνομά του και στη συνέχεια ένα ίσον. Δεξιά από το ίσον μπορούμε να γράψουμε την αριθμητική τιμή που αντιστοιχεί στο χαρακτήρα του helper ή το χαρακτήρα μέσα σε απλά εισαγωγικά. Τέλος, βάζουμε ένα ερωτηματικό (semicolon). Για παράδειγμα :

Code: Select all

quote 			= '"';
	cr			= 13;
	lf			= 10;
	single_quote		= 0x0027; //Ο χαρακτήρας ' (απλό εισαγωγικό)	

Αν ο helper αντιστοιχεί σε ένα σύνολο χαρακτήρων μπορούμε να τον ορίσουμε τοποθετώντας ανάμεσα σε αγκύλες την αρχική και τελική τιμή χωρισμένες από δύο τελείες:

Code: Select all

any_unicode_char 	= [0..0xffff] ; //Όλοι οι χαρακτήρες Unicode


Αν ο helper αντιστοιχεί σε περισσότερους από έναν χαρακτήρες ή σετ ή ακολουθίες τότε μπορούμε να χρησιμοποιήσουμε την κάθετο | για διάζευξη μεταξύ των επιλογών π.χ.

Code: Select all

letter 			= ['a'..'z'] | ['A'..'Z'];
Επίσης, μπορούμε να αφαιρέσουμε/προσθέσουμε κάποιο χαρακτήρα από μία ακολουθία/σετ. Π.χ. :

Code: Select all

non_quote		= [any_unicode_char - quote];
	permitted_in_string	= [non_quote - new_line];
	permitted_in_comment	= [any_unicode_char - new_line];
	new_line		= [cr + lf];


Τέλος, θα μπορούσαμε να χρησιμοποιήσουμε τα σύμβολα *, + και ?:
  • a* -> το a καμία, μία η περισσότερες φορές
  • a+ -> το a μία η περισσότερες φορές
  • a? -> το a μία ή καμία φορά
όπου a ένας χαρακτήρας, ένας προηγουμένως ορισμένως helper ή κάποιος συνδυασμός μέσα σε παρενθέσεις.

Έτσι πλέον το αρχείο μας έχει τη μορφή:

Code: Select all

Package subbasic;

Helpers
	any_unicode_char 	= [0..0xffff] ; //Όλοι οι χαρακτήρες Unicode
	cr			= 13;
	lf			= 10;	
	new_line		= [cr + lf];
	quote 			= '"';
	single_quote		= 0x0027; //Ο χαρακτήρας ' (απλό εισαγωγικό)
	non_quote		= [any_unicode_char - quote];
	permitted_in_string	= [non_quote - new_line];
	permitted_in_comment	= [any_unicode_char - new_line];
	letter 			= ['a'..'z'] | ['A'..'Z'];
	digit 			= ['0'..'9'];

Tokens

Ορισμός των tokens

Πλέον είναι ώρα να ορίσουμε τα tokens. Οι ορισμοί γράφονται κάτω από τη γραμμή :

Code: Select all

Tokens
με τον ίδιο τρόπο όπως για τους helpers.
Αρχικά ορίζουμε το κενό, την αλλαγή γραμμής και το tab:

Code: Select all

space			= ' ';
	newline			= cr | lf | cr lf;
	tab			= 9;

Ορίζουμε τις δεσμευμένες λέξεις(case sensitive):

Code: Select all

keyword			= 'PRINT' | 'CLS' | 'DO' | 'WHILE' | 'LOOP' | 'INPUT'
				| 'IF' | 'THEN' | 'ELSE' | 'END' | 'SELECT' | 'CASE' | 'OR'
				| 'FOR' | 'TO' | 'NEXT' | 'STEP' | 'CONST';

Μεταβλητές:

Code: Select all

string_var		= letter (letter | digit | '_')* '$';
				//γράμμα ακολουθούμενο από γράμματα ή ψηφία ή κάτω παύλες και στο τέλος $
	num_var			= letter (letter | digit | '_')*;

Σταθερές:

Code: Select all

string_const		= quote permitted_in_string* quote;
	num_const		= digit+; // ένα ή περισσότερα ψηφία

Σύμβολα:

Code: Select all

symbol			= '+'| '-' | '*' | '/' | '(' | ')' | '=' | ',' | ';'   ;
	lt			= '<';
	gt			= '>'; 

Σχόλια:

Code: Select all

comment			= single_quote permitted_in_comment*;	
Τέλος, θα ορίσουμε ένα token που θα περιλαμβάνει οποιοδήποτε άλλο χαρακτήρα:

Code: Select all

misc			= any_unicode_char ;	

Το τελικό αρχείο θα είναι αυτό:

Code: Select all

Package subbasic;

Helpers
	any_unicode_char 	= [0..0xffff] ; //Όλοι οι χαρακτήρες Unicode
	cr			= 13;
	lf			= 10;	
	new_line		= [cr + lf];
	quote 			= '"';
	single_quote		= 0x0027; //Ο χαρακτήρας ' (απλό εισαγωγικό)
	non_quote		= [any_unicode_char - quote];
	permitted_in_string	= [non_quote - new_line];
	permitted_in_comment	= [any_unicode_char - new_line];
	letter 			= ['a'..'z'] | ['A'..'Z'];
	digit 			= ['0'..'9'];

Tokens

	space			= ' ';
	newline			= cr | lf | cr lf;
	tab			        = 9;
	
	keyword			= 'PRINT' | 'CLS' | 'DO' | 'WHILE' | 'LOOP' | 'INPUT'
				| 'IF' | 'THEN' | 'ELSE' | 'END' | 'SELECT' | 'CASE' | 'OR'
				| 'FOR' | 'TO' | 'NEXT' | 'STEP' | 'CONST';


	string_var		= letter (letter | digit | '_')* '$';
				//γράμμα ακολουθούμενο από γράμματα ή ψηφία ή κάτω παύλες και στο τέλος $
	num_var			= letter (letter | digit | '_')*;
	
	string_const		= quote permitted_in_string* quote;
	num_const		= digit+; // ένα ή περισσότερα ψηφία
	
	symbol			= '+'| '-' | '*' | '/' | '(' | ')' | '=' | ',' | ';';
	lt			= '<';
	gt			= '>'; 
	
	comment			= single_quote permitted_in_comment*;

	misc			= any_unicode_char;


Το αποθηκεύουμε ως subbasic.grammar και το περνάμε ως είσοδο στο SableCC:

Code: Select all

java -jar sablecc.jar 
Μετά την εκτέλεση του SableCC πρέπει να έχει παραχθεί ένας φάκελος με το όνομα subbasic και μέσα σε αυτόν βρίσκονται οι απαραίτητες τάξεις για τη λεκτική ανάλυση.
Τώρα μένει να κατασκευάσουμε μία εφαρμογή που θα παίρνει ως είσοδο ένα αρχείο με κώδικα γραμμένο στη γλώσσα που περιγράψαμε πιο πάνω και θα παράγει ένα αρχείο html με την κατάλληλη μορφοποίηση.

Αρχικά δημιουργούμε ένα νέο κενό αρχείο με το όνομα
SubBasic2Html.java
μέσα στο φάκελο subbasic που δημιούργησε το SableCC.
Ανοίγουμε το SubBasic2Html.java και κατασκευάζουμε το βασικό σκελετό (imports, main() κλπ.):

Code: Select all

package subbasic;

import subbasic.lexer.Lexer;
import subbasic.node.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.PushbackReader;


public class SubBasic2Html{

    public static void main(String[] args){
	
    }
	
}


Τα imports από το java.io.xxxxx θα χρειαστούν λίγο πιο μετά.


Για να μην γράφουμε συνέχεια font tags κλπ. θα χρησιμοποιήσουμε CSS:

Code: Select all

private void outputHtmlHeader(PrintWriter out, String documentTitle) {
	
        out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"   \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
        out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
        out.println("");
        out.println("        <head>");
        out.println("            <title>" + documentTitle + "</title>");// Ο τίτλος του εγγράφου HTML
        out.println("            <style type=\"text/css\">");


        //Έναρξη του CSS
        out.println(".keyword{");
        out.println("    color: rgb(0,41,103);");
        out.println("    font-weight: bold;");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".num_const{");
        out.println("    color: rgb(1,104,0);");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".string_const{");
        out.println("    color: rgb(101,0,103);");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".symbol{");
        out.println("    color: rgb(103,0,1);");
        out.println("    font-weight: bold;");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".comment{");
        out.println("    color: rgb(17,78,33);");
        out.println("    font-style: italic;");
        out.println("    font-family: monospace;");
        out.println("}");
       
        out.println(".num_var, .string_var{");
        out.println("    color: rgb(0,0,0);");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".misc{");
        out.println("    color: rgb(255, 0, 0);");
        out.println("    font-family: monospace;");
        out.println("}");
           
        //Τέλος του CSS

        out.println("            </style>");
        out.println("        </head>");
        out.println("");
        out.println("        <body>");
        out.println("            <p>");
        out.println("");
    }

Αν παρατηρήσουμε λίγο τα αρχεία που έχει παράγει το SableCC θα δούμε οτι για κάθε token έχει παραχθεί μία τάξη με το όνομα του token με ένα κεφαλαίο "T" στην αρχή. Οπότε χρησιμοποιώντας το όνομα της τάξης και την instanceof μπορούμε να καθορίσουμε την τάξη του token και να παράγουμε τον αντίστοιχο κώδικα html (θα χρησιμοποιήσουμε το span tag για την εφαρμογή του CSS):

Code: Select all

private void outputHtmlForToken(Token token, PrintWriter out) {
        Class cls = token.getClass();

        String tokenHtmlClass = "";
        if (token instanceof TComment) {
            tokenHtmlClass = "comment";       
        } else if (token instanceof TKeyword) {
            tokenHtmlClass = "keyword";
        } else if (token instanceof TSymbol) {
            tokenHtmlClass = "symbol";        
        } else if (token instanceof TLt) {
            tokenHtmlClass = "lt";
        } else if (token instanceof TGt) {
            tokenHtmlClass = "gt";
        } else if (token instanceof TNumConst) {
            tokenHtmlClass = "num_const";
        } else if (token instanceof TStringConst) {
            tokenHtmlClass = "string_const";
        } else if (token instanceof TNumVar) {
            tokenHtmlClass = "num_var";
        } else if (token instanceof TStringVar) {
            tokenHtmlClass = "string_var";
        } else if (token instanceof TMisc) {
            tokenHtmlClass = "misc";
        } else if (token instanceof TNewline) {
            tokenHtmlClass = "lineEnd";
        } else if (token instanceof TSpace) {
            tokenHtmlClass = "space";
        } else if (token instanceof TTab) {
            tokenHtmlClass = "tab";
        } else {
            tokenHtmlClass = "-";
        }

        if (tokenHtmlClass.equals("-")) {
            out.print(token.getText());
        } else if (tokenHtmlClass.equals("space")) {
            out.print("&nbsp;");
        } else if (tokenHtmlClass.equals("tab")) {
            out.print("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");        
        } else if (tokenHtmlClass.equals("lt")) {
            out.print("<span class=\"symbol\"><</span>"); // και τα '<' και '>' θα χρησιμοποιήσουν
        } else if (tokenHtmlClass.equals("gt")) {            // το style της class symbol από το CSS
            out.print("<span class=\"symbol\">></span>");        
        } else if (tokenHtmlClass.equals("lineEnd")) {
            out.println("<br/>");
        } else if (tokenHtmlClass.equals("comment")) { //πρέπει να αντικαταστήσουμε το "'" με "'"
            String s = token.getText();
            out.print("<span class=\"comment\">'"+s.substring(1,s.length())+"</span>");             
        } else {
            out.print("<span class=\"" + tokenHtmlClass + "\">" + token.getText() + "</span>");
        }


    }
Μία μέθοδος που θα κλείνει το html έγγραφό μας:

Code: Select all

private void outputHtmlFooter(PrintWriter out) {
        out.println("");
        out.println("            </p>");
        out.println("        </body>");
        out.println("");
        out.println("</html>");
        out.println("");


    }

και τέλος, μία μέθοδος που θα τα συνδυάζει όλα αυτά ώστε να παραχθεί το τελικό έγγραφο:

Code: Select all

public void generateHtml(InputStreamReader input, File output, String title) {
        try {
            Lexer lexer = new Lexer(new PushbackReader(input));

            PrintWriter p = new PrintWriter(output);
            outputHtmlHeader(p, title);

            Token token = lexer.next();
            while (!token.getText().equals("")) {

                String s = new String();
                outputHtmlForToken(token, p);
                token = lexer.next();
            }

            outputHtmlFooter(p);

            p.flush();
            p.close();
        } catch (Exception e) {
            System.err.println(e);
        }
    }

Το τελευταίο βήμα είναι να γράψουμε λίγες γραμμές κώδικα στη main ώστε να δημιουργήσουμε ένα FileReader και να προσδιορίσουμε ένα αρχείο εξόδου και ένα τίτλο για το τελικό έγγραφο (π.χ. μέσω των παραμέτρων εκτέλεσης του προγράμματος):

Code: Select all

public static void main(String[] args){
        if ((args.length != 3)) {
            System.out.println("-Invalid parameters list.");
            System.exit(0);
        }

        SubBasic2Html converter = new SubBasic2Html();       

        try {
            converter.generateHtml(new FileReader(new File(args[0])), new File(args[1]), args[2]);
        } catch (Exception e) {
        }	
    }

Το τελικό αρχείο SubBasic2Html.java θα είναι αυτό:

Code: Select all

package subbasic;

import subbasic.lexer.Lexer;
import subbasic.node.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.PushbackReader;


public class SubBasic2Html{

	private void outputHtmlHeader(PrintWriter out, String documentTitle) {
	
        out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"   \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
        out.println("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
        out.println("");
        out.println("        <head>");
        out.println("            <title>" + documentTitle + "</title>");// Ο τίτλος του εγγράφου HTML
        out.println("            <style type=\"text/css\">");


        //Έναρξη του CSS
        out.println(".keyword{");
        out.println("    color: rgb(0,41,103);");
        out.println("    font-weight: bold;");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".num_const{");
        out.println("    color: rgb(1,104,0);");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".string_const{");
        out.println("    color: rgb(101,0,103);");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".symbol{");
        out.println("    color: rgb(103,0,1);");
        out.println("    font-weight: bold;");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".comment{");
        out.println("    color: rgb(17,78,33);");
        out.println("    font-style: italic;");
        out.println("    font-family: monospace;");
        out.println("}");
       
        out.println(".num_var, .string_var{");
        out.println("    color: rgb(0,0,0);");
        out.println("    font-family: monospace;");
        out.println("}");

        out.println(".misc{");
        out.println("    color: rgb(255, 0, 0);");
        out.println("    font-family: monospace;");
        out.println("}");
           
        //Τέλος του CSS

        out.println("            </style>");
        out.println("        </head>");
        out.println("");
        out.println("        <body>");
        out.println("            <p>");
        out.println("");
    }
    
    
    
    private void outputHtmlForToken(Token token, PrintWriter out) {
        Class cls = token.getClass();

        String tokenHtmlClass = "";
        if (token instanceof TComment) {
            tokenHtmlClass = "comment";       
        } else if (token instanceof TKeyword) {
            tokenHtmlClass = "keyword";
        } else if (token instanceof TSymbol) {
            tokenHtmlClass = "symbol";        
        } else if (token instanceof TLt) {
            tokenHtmlClass = "lt";
        } else if (token instanceof TGt) {
            tokenHtmlClass = "gt";
        } else if (token instanceof TNumConst) {
            tokenHtmlClass = "num_const";
        } else if (token instanceof TStringConst) {
            tokenHtmlClass = "string_const";
        } else if (token instanceof TNumVar) {
            tokenHtmlClass = "num_var";
        } else if (token instanceof TStringVar) {
            tokenHtmlClass = "string_var";
        } else if (token instanceof TMisc) {
            tokenHtmlClass = "misc";
        } else if (token instanceof TNewline) {
            tokenHtmlClass = "lineEnd";
        } else if (token instanceof TSpace) {
            tokenHtmlClass = "space";
        } else if (token instanceof TTab) {
            tokenHtmlClass = "tab";
        } else {
            tokenHtmlClass = "-";
        }

        if (tokenHtmlClass.equals("-")) {
            out.print(token.getText());
        } else if (tokenHtmlClass.equals("space")) {
            out.print("&nbsp;");
        } else if (tokenHtmlClass.equals("tab")) {
            out.print("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");        
        } else if (tokenHtmlClass.equals("lt")) {
            out.print("<span class=\"symbol\"><</span>"); // και τα '<' και '>' θα χρησιμοποιήσουν
        } else if (tokenHtmlClass.equals("gt")) {            // το style της class symbol από το CSS
            out.print("<span class=\"symbol\">></span>");        
        } else if (tokenHtmlClass.equals("lineEnd")) {
            out.println("<br/>");
        } else if (tokenHtmlClass.equals("comment")) { //πρέπει να αντικαταστήσουμε το "'" με "'"
            String s = token.getText();
            out.print("<span class=\"comment\">'"+s.substring(1,s.length())+"</span>");             
        } else {
            out.print("<span class=\"" + tokenHtmlClass + "\">" + token.getText() + "</span>");
        }


    }
    
    
    private void outputHtmlFooter(PrintWriter out) {
        out.println("");
        out.println("            </p>");
        out.println("        </body>");
        out.println("");
        out.println("</html>");
        out.println("");


    }
    
    
    
    
    public void generateHtml(InputStreamReader input, File output, String title) {
        try {
            Lexer lexer = new Lexer(new PushbackReader(input));

            PrintWriter p = new PrintWriter(output);
            outputHtmlHeader(p, title);

            Token token = lexer.next();
            while (!token.getText().equals("")) {

                String s = new String();
                outputHtmlForToken(token, p);
                token = lexer.next();
            }

            outputHtmlFooter(p);

            p.flush();
            p.close();
        } catch (Exception e) {
            System.err.println(e);
        }
    }
    
    
    public static void main(String[] args){
        if ((args.length != 3)) {
            System.out.println("-Invalid parameters list.");
            System.exit(0);
        }

        SubBasic2Html converter = new SubBasic2Html();       

        try {
            converter.generateHtml(new FileReader(new File(args[0])), new File(args[1]), args[2]);
        } catch (Exception e) {
        }	
    }
	
}
Κάνουμε compile το πρόγραμμά μας (από το φάκελο στον οποίο βρίσκεται το αρχείο subbasic.grammar) με:

Code: Select all

javac subbasic/*.java
και τρέχουμε την εφαρμογή με:

Code: Select all

java subbasic.SubBasic2Html <αρχείο_εισόδου> <αρχείο_εξόδου> <τίτλος_εγγράφου>

π.χ. αν έχουμε ένα αρχείο test.bas με τον κώδικα:

Code: Select all

CLS
DO
    INPUT "Enter the first number: ", A
    INPUT "Enter the second number: ", B
    PRINT "The answer is: "; A * B
    
    INPUT "Would you like to do it again? ", Answer$
    FirstLetter$ = LEFT$(Answer$, 1)
LOOP WHILE FirstLetter$="y" OR FirstLetter$="Y"
και τρέξουμε το πρόγραμμά μας ως εξής:

Code: Select all

java subbasic.SubBasic2Html test.bas out.html "SubBasic Test Code"
Θα πάρουμε αυτό το αρχείο html ώς αποτέλεσμα (out.html):

Image

Code: Select all

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

        <head>
            <title>SubBasic Test Code</title>
            <style type="text/css">
.keyword{
    color: rgb(0,41,103);
    font-weight: bold;
    font-family: monospace;
}
.num_const{
    color: rgb(1,104,0);
    font-family: monospace;
}
.string_const{
    color: rgb(101,0,103);
    font-family: monospace;
}
.symbol{
    color: rgb(103,0,1);
    font-weight: bold;
    font-family: monospace;
}
.comment{
    color: rgb(17,78,33);
    font-style: italic;
    font-family: monospace;
}
.num_var, .string_var{
    color: rgb(0,0,0);
    font-family: monospace;
}
.misc{
    color: rgb(255, 0, 0);
    font-family: monospace;
}
            </style>
        </head>

        <body>
            <p>

<span class="keyword">CLS</span><br/>
<span class="keyword">DO</span><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">INPUT</span>&nbsp;<span class="string_const">"Enter the first number: "</span><span class="symbol">,</span>&nbsp;<span class="num_var">A</span><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">INPUT</span>&nbsp;<span class="string_const">"Enter the second number: "</span><span class="symbol">,</span>&nbsp;<span class="num_var">B</span><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">PRINT</span>&nbsp;<span class="string_const">"The answer is: "</span><span class="symbol">;</span>&nbsp;<span class="num_var">A</span>&nbsp;<span class="symbol">*</span>&nbsp;<span class="num_var">B</span><br/>

&nbsp;&nbsp;&nbsp;&nbsp;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">INPUT</span>&nbsp;<span class="string_const">"Would you like to do it again? "</span><span class="symbol">,</span>&nbsp;<span class="string_var">Answer$</span><br/>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="string_var">FirstLetter$</span>&nbsp;<span class="symbol">=</span>&nbsp;<span class="string_var">LEFT$</span><span class="symbol">(</span><span class="string_var">Answer$</span><span class="symbol">,</span>&nbsp;<span class="num_const">1</span><span class="symbol">)</span><br/>
<span class="keyword">LOOP</span>&nbsp;<span class="keyword">WHILE</span>&nbsp;<span class="string_var">FirstLetter$</span><span class="symbol">=</span><span class="string_const">"y"</span>&nbsp;<span class="keyword">OR</span>&nbsp;<span class="string_var">FirstLetter$</span><span class="symbol">=</span><span class="string_const">"Y"</span><br/>

<br/>

            </p>
        </body>

</html>


Re: Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Mon Mar 16, 2009 10:40 pm
by sandra
Νομίζω πως έπρεπε να κάνεις εσύ το φροντιστήριο σήμερα. Αν δεν ήταν venus θα είχε γίνει ήδη bookmarked από μένα. Σε ευχαριστούμε! :)

Re: Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Tue Mar 17, 2009 2:31 am
by elsupreme
...τα λόγια είναι περιττά...
Respect etc. :cool:

Re: Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Tue Mar 17, 2009 3:04 am
by para
Απλά wow :shock:
Αρχηγός! :king:

Και χρήσιμα και ενδιαφέροντα :smt023

--
Spoiler: εμφάνιση/απόκρυψη
Αν δε μου διέφυγε κάποιο σημείο του κώδικα, το [code]java subbasic.SubBasic2Html test.bas out.html "SubBasic Test Code"[/code] θα πρέπει να μας δίνει [code]<title>SubBasic Test Code</title>[/code] (κι όχι QBasic), σωστά;

Re: Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Wed Mar 18, 2009 4:14 pm
by cyberpython
para wrote:
Αν δε μου διέφυγε κάποιο σημείο του κώδικα, το

Code: Select all

  java subbasic.SubBasic2Html test.bas out.html "SubBasic Test Code"
θα πρέπει να μας δίνει

Code: Select all

 <title>SubBasic Test Code</title>
(κι όχι QBasic), σωστά;
Έχεις απόλυτο δίκιο - είχα ξεχάσει μία τελεία στην παραγωγή του CSS και ξαναέτρεξα το παράδειγμα με άλλο τιτλο :-D ....

-------------------------------------------

Μόλις παρατήρησα οτι ο κώδικας HTML που παράγεται από την παραπάνω εφαρμογούλα περνάει και το validation του W3C ως XHTML 1.0 Strict! :cool: :lol:

Re: Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Fri Mar 20, 2009 10:10 am
by vangos
Μήπως να έμπαινε καλύτερα στο (ή και στο) Wiki?

Re: Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Fri Mar 20, 2009 10:50 am
by The Punisher
δεν υπάρχει wiki πλέον, γιατί δεν έβρισκε λόγο ύπαρξης

Re: Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Fri Mar 27, 2009 5:28 pm
by StormRider
Μπράβο, ευχαριστούμε! :smt023

Re: Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Mon Mar 30, 2009 12:56 pm
by Zifnab
The Punisher wrote:δεν υπάρχει wiki πλέον, γιατί δεν έβρισκε λόγο ύπαρξης
Τουλάχιστον έτσι όπως θα ήταν δομημένο δεν είχε λόγο ύπαρξης...

Re: Γράφοντας ένα Lexer με το SableCC 3.2

Posted: Mon Mar 29, 2010 11:01 am
by rose
Using SableCC in Eclipse

Εχω βρει ενα plugin για το to SableCC. Το πρόβλημα μου ειναι οτι πάντα προσθέτει στο ονομα των packages που δημιουργεί την λέξη output.
Δηλαδή αν εγώ στο α.grammar γράψω Package rose; Τα generated packages θα εχουν το ονομα output.rose. Το εχει δει κανένας?


http://cse.unl.edu/~kdeng/myweb/fun/code/code.html