Creating a Java Interpreter for android?

Asked

Viewed 723 times

8

I am developing an android application where it will be possible to write a java code and run... something similar to the visualg. then I did something different than most tutorials on compilers and pseudocodes would say... where a machine code written in Assembly would have to be generated, or maybe just a java bytecode...
in pc :(text --> intermediate code --> machine code --> Executes)
on android:(*. java --> java.class --> classes.Dex --> ART interprets -->Runs)

but as the creation of such a compiler is too complex I decided to make an interpreter at runtime, and what it does is compile the text into "micro-operations", such as Sum, Subtraction, Multiplication, Println, etc... then he does the following:
(text --> creates micro-generations --> performs each micro-generation)

the structure of the interpreter is similar to this:Diagrama geral mal modelado now I wanted to ask you if there’s a way to optimize this code, doing some tests with the emulator, I saw that it is possible to run up to 79 million micro-operations in 1 second, which is about 12 times slower than a real 1.0 gigaherts processor. but wanted to find out if the way I put it all together is really something new or if there are interpreted languages that work in a similar way to this (should consume a lot of memory RAM)
any suggestions on how to improve/optimize this project?
example of one of the executable classes (all others are equal only changes the type and the operator)

public final class SomarDoubles extends Executavel {

final WDouble a;
final WDouble b;
final WDouble c;
public SomarDoubles(WDouble a, WDouble b,WDouble c)
{
this.a = a;
this.b = b;
this.c = c;
}
@Override
final public void Executar() {
    c.valor = a.valor+b.valor;
}
}

example of a type class (same thing, only changes the value contained):

public final class WByte extends Valor {
byte valor = 0;
public WByte(byte v) {
    this.valor = v;
}
// métodos de casting:
@Override
final public short Short() {
    return valor;
}

@Override
final public char Char() {
    return (char)valor;
}
// etc...
}

and the code when executing (the compilation is done before and sends the array and instructions)

public class Executar extends Thread{

private final Executavel[] instrucoes;
private final int length;
public static int i=0;
public Executar(Executavel[] instrucoes)
{
super();
this.instrucoes = instrucoes;
length = instrucoes.length;
}

@Override
final public void run()
{   
i=0;    
long delay = System.nanoTime();
long k=0;
try {
    while(i<length)
    {
    instrucoes[i++].Executar();
    k++;
    }
} catch (Exception e) {
    e.printStackTrace();
}
long agora=  System.nanoTime();
double tempo = (agora-delay);
System.out.println("emulado : executou "+k+" instrucoes em "+tempo/1000000.0d+" milisegundos ("+tempo/1000000000.0d+" segundos), o que d� \n"
        + " aproximadamente "+tempo/k+" nanosegundos por instrucao");

}
}

1 answer

6


Compiler vs. Interpreter

There is no problem in creating interpreters for a given language or a subset of it.

Your approach is correct within some conditions:

  1. Your interpreter really meets your needs. Certainly not enough to emulate the entire Java language in this way, but a set of simplified instructions.
  2. The interpreter meets your performance requirements. Certainly interpreting or emulating is always slower than having a compiled code, but in many cases this will be imperceptible to the user if it is used sparingly at specific points and storing the results in cache when possible.

Precisely define the scope of your language

From what I understand, you don’t need all the features of Java, but mainly some expressions and also print commands.

In this case, specify exactly the syntax and grammar of this Java-derived sublinguage and consider whether it will be possible to implement all the necessary functionalities.

I don’t know how you are interpreting the code, but even if you are not creating a compiler, you will use compiler theory, because you must have created or will need to create lexical, syntactic and semantic parsers.

If, by any chance, you’re interpreting the language using regex, split or substring will have problems in various situations, for example if there are multiple levels of parentheses in the mathematical expressions.

Anyway, creating an interpreter, even if only for a very small scope as mathematical expressions is not totally trivial unless you have a good theoretical or at least practical basis on the subject.

See an example of an interpreter of expressions in my other answer.

Other options

If you really need to execute some more complex code and don’t want to reinvent the wheel, consider using an existing interpreter.

There are several libraries in Java that can help you:

Rhino

This is Javascript interpreter implemented in Java. See an article on how to use it on Android. Here’s another.

Here an example:

public void runScript() {
    // Get the JavaScript in previous section
    String source = getScriptFromServer();
    String functionName = "hello";
    Object[] functionParams = new Object[] { "Android" };

    // Every Rhino VM begins with the enter()
    // This Context is not Android's Context
    Context rhino = Context.enter();

    // Turn off optimization to make Rhino Android compatible
    rhino.setOptimizationLevel(-1);
    try {
        Scriptable scope = rhino.initStandardObjects();

    // This line set the javaContext variable in JavaScript
    ScriptableObject.putProperty(scope, "javaContext", Context.javaToJS(androidContextObject, scope));

        // Note the forth argument is 1, which means the JavaScript source has
    // been compressed to only one line using something like YUI
        rhino.evaluateString(scope, RHINO_LOG + source, "ScriptAPI", 1, null);

        // We get the hello function defined in JavaScript
        Function function = (Function) scope.get(functionName, scope);

    // Call the hello function with params
        NativeObject result = (NativeObject) function.call(rhino, scope, scope, functionParams));
    // After the hello function is invoked, you will see logcat output

    // Finally we want to print the result of hello function
    String foo = (String) Context.jsToJava(result.get("foo", result), String.class);
    log(foo);
    } finally {
    // We must exit the Rhino VM
        Context.exit();
    }
}

Java Expression Language (JEXL)

The JEXL is a library that implements a JSTL-based expression language, similar to what we have in Jsps.

Example:

// Assuming we have a JexlEngine instance initialized in our class named 'jexl':
// Create an expression object for our calculation
String calculateTax = taxManager.getTaxCalc(); //e.g. "((G1 + G2 + G3) * 0.1) + G4";
Expression e = jexl.createExpression( calculateTax );

// populate the context
JexlContext context = new MapContext();
context.set("G1", businessObject.getTotalSales());
context.set("G2", taxManager.getTaxCredit(businessObject.getYear()));
context.set("G3", businessObject.getIntercompanyPayments());
context.set("G4", -taxManager.getAllowances());
// ...

// work it out
Float result = (Float)e.evaluate(context);

Velocity

The Apache Velocity is a template library, but can also be used to execute expressions and snippets of code in a special syntax

The documentation says how can you use it within a Java application, as well as providing a reference on language.

Example of how to run Velocity:

/* first, we init the runtime engine.  Defaults are fine. */

Velocity.init();

/* lets make a Context and put data into it */

VelocityContext context = new VelocityContext();

context.put("name", "Velocity");
context.put("project", "Jakarta");

/* lets render a template */

StringWriter w = new StringWriter();

Velocity.mergeTemplate("testtemplate.vm", context, w );
System.out.println(" template : " + w );

/* lets make our own string to render */

String s = "We are using $project $name to render this.";
w = new StringWriter();
Velocity.evaluate( context, w, "mystring", s );
System.out.println(" string : " + w );

MVEL

The Apache MVEL is another expression language library.

You may know the language syntax in documentation, besides integrate it into your program easily.

Example:

 String expression = "foobar > 99";

 Map vars = new HashMap();
 vars.put("foobar", new Integer(100));

 // We know this expression should return a boolean.
 Boolean result = (Boolean) MVEL.eval(expression, vars);

 if (result.booleanValue()) {
     System.out.println("It works!");
}      

MVEL allows you to "compile" the expression for better performance if reused and also force check of variable types to avoid errors during execution.

  • 1

    Thanks for responding to me so quickly and with multiple sources. will certainly interpret is slower, but will be used with "moderation", after all will run in an android application with just learning purpose, if the code takes 0.4 nanoseconds or 60 nanoseconds to sum 2 integers does not make much difference...

  • 1

    if you want to know how I interpret the notation of the Reverse Polish Notation (does not use parentheses) converter: http://andreinc.net/2010/10/05/converting-infix-to-rpn-shunting-yard-algorithm/ resolver: http://www.java2s.com/Code/Java/Collections-Data-Structure/Reversepolishnotation.htm I just adapted and expanded these two algorithms so that they could support most programming operators ( =, <= ,>= , ==, *=, /=, etc... ) and instead of calculating save the operation as an object of one of the child types of Executable with the values a,b, and c, which are added to the array

Browser other questions tagged

You are not signed in. Login or sign up in order to post.