Avatar billede jespersahner Nybegynder
01. februar 2005 - 15:37 Der er 13 kommentarer og
1 løsning

Eksekvering af kode indlæst fra JTextArea

Jeg bruger et JTextArea-objekt til at indlæse Java-kode, og teksten læses med getText().

Men hvordan eksekverer jeg koden efterfølgende i min Java-session?
Avatar billede jakoba Nybegynder
01. februar 2005 - 16:31 #1
Du skriver en lille Java compiler og lader det kompilere det brugeren har skrevet :-))

Beklager, men når et javaprogram en gang er kompileret blive compilerprogrammet lagt væk (det fylder en hulens masse), og den relativt lille Java Virtuelle Maskine udfører så. Det er ikke som i script sprogene hvor programmet løbende vliver kompileret of udført, der er det nemt lige at bede om at få eval'et en streng. I Java må du selv kompilere strengen.

mvh JakobA
Avatar billede jespersahner Nybegynder
01. februar 2005 - 18:13 #2
->jakoba: Tak for dit input.

At skrive en lille Java compiler er vel ikke helt enkelt, men du er vel lidt ironisk her :-)

Faktisk har jeg lidt kig på script-programmet BeanShell, som gør lidt af det jeg efterlyser.

Men det er vel en ret generel problemstilling vi taler om her eller..? - altså forholdet omkring successiv kompilering og eksekvering.
Avatar billede jword Nybegynder
01. februar 2005 - 22:10 #3
Hvis du har tools.jar på din classpath kan du compilere med classen:
com.sun.tools.javac.Main
Den har en metode der hedder:
compile(String[] s)
Avatar billede jespersahner Nybegynder
01. februar 2005 - 23:08 #4
->jword: Ja, det er rigtigt, og man også kalde 'javac' med Runtime.exec().

Men er det nu så enkelt? For så vidt argumentet er en fil, der indeholder en lovlig definition af en klasse, går det godt, men hvis filen f.eks. indeholder et fritstående statement såsom:

Integer i=new Integer(5);

- virker det ikke. I så fald skal der ske en form for "indpakning" af koden i en klasse.
Avatar billede jakoba Nybegynder
02. februar 2005 - 14:25 #5
Hvis du jåber på at det vil vær muligt at sige:
    compile( "Integer iUnik=new Integer(5);" );
og derefter bruge den nye vaiabel:
    if ( iUnik.intValue() > 4 ) { ... }

er jeg ret sikker på at det skal gøres 'manuelt'. de to linier kompilerer i helt forskellige scopes, så de to anvendelser af variabelnavnet 'iUnik' kan slet ikke se hinanden.
Avatar billede jespersahner Nybegynder
02. februar 2005 - 14:43 #6
->jakoba: Enig. Men det er den form for funktionalitet jeg er på udkig efter eller lidt mere generelt: Jeg har et ønske om at kunne foretage successiv/step-vis kompilering og eksekvering i en åben Java-session.

Jeg har som nævnt lidt kig på "BeanShell", som faktisk (blandt andet) kan præcis dette, så vidt jeg kan overskue, men jeg vil også gerne forstå, hvordan man teknisk griber det an. Det er nok noget med reflections, tænker jeg.
Avatar billede jakoba Nybegynder
02. februar 2005 - 14:58 #7
Jeg lavede nedenstående som demo engang. Det har den fordel at det er kort, men den bagdel at det er meget rudimentært. 2-bogstav variabelnavne, så max 26 variable og den eneste datatype er int, de eneste operatorer er =, *, /, -, + og parenteser.

men måske den kan bruges som skelet til det du vil have :)

/*
**  implement a very small interpreter
*/

import java.util.*;
import java.io.*;


class ParseError extends Exception {
    public ParseError( String message ) {
        super( message );
    }
} // endclass ParseError

class RuntimeError extends Exception {
    public RuntimeError( String message ) {
        super( message );
    }
} // endclass RuntimeError


interface Element {  // the groups and atoms of a statement
    public int getValue( ) throws RuntimeError;
    public int setValue( int value ) throws RuntimeError;
} //end interface Element


class Value implements Element {

    private int value;

    public Value ( int value ) { this.value = value; }
   
    public int getValue( ) throws RuntimeError {
        return this.value;
    }
   
    public int setValue( int value ) throws RuntimeError {
        throw new RuntimeError( "attempt to change the value of a constant." );
    }

} //endclass Value


class Variable implements Element {
   
    private static final Variable[] vt = new Variable[26]; // the nametable
   
    private int value;
    private boolean initialized;
    private String name;
   
    private Variable ( String name ) { // instances created only by getVariable()
        this.initialized = false;
        this.name = name;
    }
   
    static Variable getVariable( String name ) {
        for ( int i=0; i<vt.length; i++ ) {      // return existing variable
            if ( vt[i] != null && vt[i].name.equals(name) ) return vt[i];
        }
        for ( int i=0; i<vt.length; i++ ) {      // or create a new one
            if ( vt[i] == null  ) return ( vt[i] = new Variable( name ) ); 
        }
        throw new Error( "Impossible error. More that 26 variables used." );
    }
   
    public int getValue( ) throws RuntimeError {
        if ( this.initialized ) return this.value;
        throw new RuntimeError( "variable " +name +" not initialized." );
    }

    public int setValue( int value ) throws RuntimeError {
        this.initialized = true;
        return ( this.value = value );
    }

} //endclass variable


class Triple implements Element {
   
    private Element operand1, operand2;
    private char  operator; 
   
    public Triple ( Element op1, char operator, Element op2 ) {
        this.operand1 = op1;
        this.operator = operator;
        this.operand2 = op2;
    }
       
    public int getValue() throws RuntimeError {
        switch ( this.operator ) {
            case '=': return operand1.setValue( operand2.getValue() );
            case '+': return operand1.getValue() + operand2.getValue();
            case '-': return operand1.getValue() - operand2.getValue();
            case '*': return operand1.getValue() * operand2.getValue();
            case '/': {
                int divisor = operand2.getValue();
                if ( divisor != 0 ) return operand1.getValue() / divisor;
                throw new RuntimeError( "attempted division by 0.'" );
            }
            default:
                throw new RuntimeError( "unknown operator code '" +operator +"'." );
        }
    }

    public int setValue( int value ) throws RuntimeError {
        throw new RuntimeError( "attempt to change the value of an expression." );
    }

} // endclass Triple



        //  adds a one-token lookahead
class Tokenizer extends StringTokenizer {
   
    String next;
   
    public Tokenizer( String s ) {
        super( s );
        this.next = ( hasMoreTokens() ) ? nextToken() : " ";
    }
   
    char peekNextChar() {
        return this.next.charAt(0);
    }
   
    boolean skipPredicted( String s ) {
        if ( s.equals(next) ) {
            this.next = ( hasMoreTokens() ) ? nextToken() : " ";
            return true;
        }
        return false;
    }
   
    String fetchNext( ) {
        String temp = this.next;
        this.next = ( hasMoreTokens() ) ? nextToken() : " ";
        return temp;
    }
       
} // endclass Tokenizer


        // inplement the simple syntax
class Compile {
   
    static Tokenizer t;

    static final String names = "ABCDEFGHIJKLMNOPQRSTUVWXUZ";
    static final String digits = "0123456789";
   
    static Element lValue() throws ParseError {
        String name = t.fetchNext();
        if ( names.indexOf(name)  >= 0 ) return Variable.getVariable( name );
        if ( digits.indexOf(name) >= 0 ) return new Value( Integer.parseInt( name ) );
        throw new ParseError( "bad left side variable '" +name +"'." );
    }
   
    static Element parenthetical() throws ParseError {
        Element temp = expression();
        if ( t.skipPredicted(")") ) return temp;
        throw new ParseError( "bad parenthetical, ')' expected." );
    }
   
    static Element factor() throws ParseError {
        String nxt = t.fetchNext();
        if ( nxt.equals( "(" ) ) return parenthetical();
        if ( names.indexOf(nxt)  >= 0 ) return Variable.getVariable( nxt );
        if ( digits.indexOf(nxt) >= 0 ) return new Value( Integer.parseInt( nxt ) );
        throw new ParseError( "Bad Factor at '" +nxt +"'." );
    }

    static Element term() throws ParseError {
        Element op1 = factor();
        char opr = t.peekNextChar();
        while ( opr == '*' || opr == '/' ) {
            t.fetchNext();
            op1 = new Triple( op1, opr, factor() );
            opr = t.peekNextChar();
        }
        return op1;
    }

    static Element expression() throws ParseError {
        Element op1 = term();
        char opr = t.peekNextChar();
        while ( opr == '+' || opr == '-' ) {
            t.fetchNext();
            op1 = new Triple( op1, opr, term() );
            opr = t.peekNextChar();
        }
        return op1;
    }

    static Element statement( String s ) {
        t = new Tokenizer( s );
        try {
            Element op1 = lValue();
            Element op2;
            if ( t.skipPredicted("=") ) {
                op2 = expression();
            } else {
                throw new ParseError( "no assignment in statement." );
            }
            if ( t.skipPredicted(";") ) return new Triple( op1, '=', op2 );
            throw new ParseError( "missing ';' at end of statement." );
        } catch ( ParseError e ) {
            System.out.println( "error in line: " +s );
            System.out.println( "Parsing error: " +e.getMessage() );
        }
        return new Value( -666 );
    }
   
} // endclass Compile


        // the main class compile and execute a file containing a simple program
class CalcFunction {
   
    static final int MAX = 100;    // max 100 statements
    static Element[] statements = new Element[MAX];
    static String[] lines    = new String[MAX];
    static int programLength = 0;
   
    static String readLine( InputStream reader ) {
        String line = "";
        try {
            int ch;
            while ( (ch=reader.read()) != 13 && ch != -1 ) {
                if ( ch != 10 ) line += (char)ch;
            }
        } catch ( Exception e ) {
            System.out.print( "error in readLine." );
        }
        return line;
    }
   
    static void compile( String fileName ) {
        FileInputStream fil;
        try {
            fil = new FileInputStream( fileName );
        } catch ( Exception e ) {
            System.out.print( "no such file found." );
            throw new Error( "compilation terminated in error." );
        }
        String line = readLine( fil );
        while ( ! ( line.equals("") ) ) {
            lines[programLength] = line;
            statements[ programLength++ ] = Compile.statement( line );
            line = readLine( fil );
        }
    }

    static void execute( ) {
        for ( int i=0; i<programLength; i++ ) {
            System.out.println( i +": " +lines[i] );
            try {
                System.out.println( "      calculated value is " +statements[i].getValue() );
            } catch ( RuntimeError e ) {
                System.out.println ( "statement nr " +i +" failed: " +e.getMessage() );
            }
        }
    }

    public static void main( String[] args ) {
        String fileName = "c:/testprogram.txt";
       
        System.out.println( "\n--- Compiling '" +fileName +"'." );
        compile( fileName );
       
        System.out.println( "\n\n--- Running '" +fileName +"'." );
        execute( );
    }
   
} // endclass CalcFunction


/*        file  c:/testprogram.txt  contain:

A = 5 ;
B = A * 2 + 9 ;
C = A + B * ( 1 - 1 ) ;
C = A + B / ( 1 - 1 ) ;  // divide by zero
C = 7 + C ;
D = B + C + A * 2 ;
E = A * ( B + C ) ;
F = E / 5 ;
G = A + ( B + ( C + ( D + ( E + ( F * 2 ) ) ) ) ) ;
9 = 4 * A ;            // assign to a number

*/

/*        and gives the below output:

--- Compiling 'c:/testprogram.txt'.
error in line: 9 = 4 * A ;
Parsing error: bad left side variable '9'.

--- Running 'c:/testprogram.txt'.
0: A = 5 ;              should give 5
      calculated value is 5
1: B = A * 2 + 9 ;      should give 19
      calculated value is 19
2: C = A + B * ( 1 - 1 ) ;
      calculated value is 5
3: C = A + B / ( 1 - 1 ) ;
statement nr 3 failed: attempted division by 0.'
4: C = 7 + C ;
      calculated value is 12
5: D = B + C + A * 2 ;
      calculated value is 41
6: E = A * ( B + C ) ;
      calculated value is 155
7: F = E / 5 ;
      calculated value is 31
8: G = A + ( B + ( C + ( D + ( E + ( F * 2 ) ) ) ) ) ;
      calculated value is 294
9: 9 = 4 * A ;
      calculated value is -666

Process Exit.

*/

mvh JakobA
Avatar billede jespersahner Nybegynder
02. februar 2005 - 17:42 #8
->jakoba: Tak for dit input.

En yderligere kommentar:
Det er for så vidt ikke problematisk at kompilere kode i flere omgange ved f.eks. at anvende com.sun.tools.javac.Main.compile() (eller 'javac' + Runtime.exec()) og reflections:
...
String klasse;
Class c = Class.forName(klasse);
Object o = c.newInstance();
Method m = c.getDeclaredMethod("main", ...);
m.invoke(o, ...);
...

Eksempel:
De enkelte steps tænkes lagret i eksterne filer, som dynamisk indlæses, kompileres og evt. eksekveres, hvis main()-metoden findes. Yderligere foregår dette i en applikation med en tekst-box, hvor koden tastes ind eller filnavnene angives.

/* 1. step: */
public class SampleX {
  public int i; 
  SampleX() {
    i=5;
  }
}

/* 2. step */
public class Sample {
  public static void main(String args[]) { 
    SampleX s=new SampleX();
    System.out.println(s.i);
  }
}

Dette går fint. Sample kan oprette et SampleX-objekt uden problemer.

Hvad der imidlertid ikke fungerer er, hvis man forsøger med flg.:
/* 3. step */
public class Sample2 {
  public static void main(String args[]) { 
    System.out.println(s.i);
  }
}

Her er 's.i' ikke kendt. Problemet er altså, hvordan man holder de allerede oprettede referencer "i live" i sessionen.
Avatar billede jword Nybegynder
02. februar 2005 - 20:57 #9
Du kan jo også prøve at kigge på:
http://jakarta.apache.org/bcel/index.html
Avatar billede jespersahner Nybegynder
03. februar 2005 - 09:58 #10
->jword: arne_v har tidligere vist mig et eks. med BCEL. Jeg synes det virker temmelig tungt, men det er givetvis effektivt.
Avatar billede jespersahner Nybegynder
04. februar 2005 - 14:57 #11
->jakoba: Smid lige et svar.
Avatar billede jespersahner Nybegynder
08. februar 2005 - 14:54 #12
->jakoba: Smid lige et svar.
Avatar billede jespersahner Nybegynder
10. februar 2005 - 12:04 #13
Lukker spm.
Avatar billede jespersahner Nybegynder
10. februar 2005 - 12:04 #14
Lukker spm.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester