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

Συζητήσεις για γλώσσες προγραμματισμού και θέματα σχετικά με προγραμματισμό.
Post Reply
User avatar
cyberpython
Mbyte level
Mbyte level
Posts: 654
Joined: Wed Nov 21, 2007 8:18 pm
Academic status: Alumnus/a
Gender:
Location: Αθηνα
Contact:

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

Post by cyberpython » Mon Mar 16, 2009 6:30 pm

Μιας και είμαι άρρωστος σήμερα και δεν έχω τι να κάνω ας δούμε πως μπορούμε να κατασκευάσουμε ένα μικρό 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>

Last edited by cyberpython on Tue Mar 17, 2009 3:07 pm, edited 1 time in total.
User avatar
sandra
Wow! Terabyte level
Wow! Terabyte level
Posts: 4917
Joined: Mon Oct 02, 2006 11:37 am
Academic status: Alumnus/a
Gender:
Location: στη φωλιά μου κοιτώντας ένα χωράφι με στάρι...

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

Post by sandra » Mon Mar 16, 2009 10:40 pm

Νομίζω πως έπρεπε να κάνεις εσύ το φροντιστήριο σήμερα. Αν δεν ήταν venus θα είχε γίνει ήδη bookmarked από μένα. Σε ευχαριστούμε! :)
Από εδώ κι εμπρός θα είσαι για πάντα υπεύθυνος για εκείνο που έχεις ημερώσει.
Είσαι υπεύθυνος για το τριαντάφυλλο σου...
User avatar
elsupreme
Gbyte level
Gbyte level
Posts: 1573
Joined: Mon Nov 21, 2005 10:16 pm
Academic status: N>4
Gender:

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

Post by elsupreme » Tue Mar 17, 2009 2:31 am

...τα λόγια είναι περιττά...
Respect etc. :cool:
"Must float like lotus on river... and kill old lady!"
User avatar
para
Wow! Terabyte level
Wow! Terabyte level
Posts: 3648
Joined: Sat Nov 04, 2006 2:59 am
Academic status: Alumnus/a
Gender:

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

Post by para » Tue Mar 17, 2009 3:04 am

Απλά 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), σωστά;
Γύρνα είμαι ένα άψυχο κορμί που σ' αγαπάει, αισθάνομαι στον άνεμο φτερό
Σαν μέσα σε όνειρο η ζωή με προσπερνάει, δείξε μου οίκτο μια στιγμή παρακαλώ...
#!
Κοίτα πως με κατάντησε η δική σου η αγάπη, να μη γνωρίζω από που να κρατηθώ
Στο τελευταίο της ζωής το σκαλοπάτι, Γύρνα, είμαι ένα βήμα απ' το γκρεμό...
User avatar
cyberpython
Mbyte level
Mbyte level
Posts: 654
Joined: Wed Nov 21, 2007 8:18 pm
Academic status: Alumnus/a
Gender:
Location: Αθηνα
Contact:

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

Post by cyberpython » Wed Mar 18, 2009 4:14 pm

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:
Last edited by Zifnab on Thu Mar 19, 2009 12:45 pm, edited 1 time in total.
Reason: merged posts
User avatar
vangos
Mbyte level
Mbyte level
Posts: 571
Joined: Tue Mar 16, 2004 3:14 pm
Location: Heracleia@UTA

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

Post by vangos » Fri Mar 20, 2009 10:10 am

Μήπως να έμπαινε καλύτερα στο (ή και στο) Wiki?
The Punisher
Venus Former Team Member
Posts: 7561
Joined: Thu Oct 27, 2005 1:43 pm
Academic status: Alumnus/a
Gender:
Location: Boston, MA

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

Post by The Punisher » Fri Mar 20, 2009 10:50 am

δεν υπάρχει wiki πλέον, γιατί δεν έβρισκε λόγο ύπαρξης
StormRider
Gbyte level
Gbyte level
Posts: 1040
Joined: Mon Dec 18, 2006 5:34 pm
Gender:

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

Post by StormRider » Fri Mar 27, 2009 5:28 pm

Μπράβο, ευχαριστούμε! :smt023
Image
User avatar
Zifnab
Venus Former Team Member
Posts: 7581
Joined: Tue Nov 15, 2005 2:42 am
Academic status: MSc
Gender:
Location: Connecticut
Contact:

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

Post by Zifnab » Mon Mar 30, 2009 12:56 pm

The Punisher wrote:δεν υπάρχει wiki πλέον, γιατί δεν έβρισκε λόγο ύπαρξης
Τουλάχιστον έτσι όπως θα ήταν δομημένο δεν είχε λόγο ύπαρξης...
User avatar
rose
Gbyte level
Gbyte level
Posts: 1921
Joined: Sun May 20, 2007 8:59 pm
Academic status: 4th year
Gender:

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

Post by rose » Mon Mar 29, 2010 11:01 am

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
που θα πάει θα το δουμε...
Post Reply

Return to “Προγραμματισμός”