Listing 1:
SimpleCalculator.javaimport java.util.ArrayList;
import java.util.Stack;
import java.util.StringTokenizer;
public class SimpleCalculator
implements Calculator
{
String[] _toks;
public SimpleCalculator(String expression) {
ArrayList list = new ArrayList();
StringTokenizer tokenizer =
new StringTokenizer(expression);
while (tokenizer.hasMoreTokens()) {
list.add(tokenizer.nextToken());
}
_toks = (String[]) list.toArray(
new String[list.size()]);
}
public int evaluate(int[] args)
{
Stack stack = new Stack();
for (int i=0; i < _toks.length; i++) {
String tok = _toks[i];
if (tok.startsWith("$")) {
int varnum = Integer.
parseInt(tok.substring(1));
stack.push(new Integer(args[varnum]));
} else {
char opchar = tok.charAt(0);
int op = "+-*/".indexOf(opchar);
if (op == -1) {
stack.push(Integer.valueOf(tok));
} else {
int arg2 = ((Integer) stack.pop())
.intValue();
int arg1 = ((Integer) stack.pop())
.intValue();
switch(op) {
case 0:
stack.push(new Integer(arg1 + arg2));
break;
case 1:
stack.push(new Integer(arg1 - arg2));
break;
case 2:
stack.push(new Integer(arg1 * arg2));
break;
case 3:
stack.push(new Integer(arg1 / arg2));
break;
default:
throw new RuntimeException(
"invalid operator: " + tok);
}
}
}
}
return ((Integer) stack.pop()).intValue();
}
}
Listing 2: CalculatorParser.java
import java.util.Stack;
import java.util.StringTokenizer;
public class CalculatorParser
{
public Calculator parse(String expression)
{
Stack stack = new Stack();
StringTokenizer toks =
new StringTokenizer(expression);
while (toks.hasMoreTokens()) {
String tok = toks.nextToken();
if (tok.startsWith("$")) {
int varnum = Integer.
parseInt(tok.substring(1));
stack.push(new VariableValue(varnum));
} else {
int op = "+-*/".indexOf(tok.charAt(0));
if (op == -1) {
int val = Integer.parseInt(tok);
stack.push(new ConstantValue(val));
} else {
Calculator node2 =
(Calculator) stack.pop();
Calculator node1 =
(Calculator) stack.pop();
stack.push(new Operation(tok.charAt(0),
node1, node2));
}
}
}
return (Calculator) stack.pop();
}
static class ConstantValue
implements Calculator
{
private int _value;
ConstantValue(int value) {
_value = value;
}
public int evaluate(int[] args) {
return _value;
}
}
static class VariableValue
implements Calculator
{
private int _varnum;
VariableValue(int varnum) {
_varnum = varnum;
}
public int evaluate(int[] args) {
return args[_varnum];
}
}
static class Operation
implements Calculator
{
char _op;
Calculator _arg1;
Calculator _arg2;
Operation(char op, Calculator arg1,
Calculator arg2)
{
_op = op;
_arg1 = arg1;
_arg2 = arg2;
}
public int evaluate(int args[]) {
int val1 = _arg1.evaluate(args);
int val2 = _arg2.evaluate(args);
if (_op == '+') {
return val1 + val2;
} else if (_op == '-') {
return val1 - val2;
} else if (_op == '*') {
return val1 * val2;
} else if (_op == '/') {
return val1 / val2;
} else {
throw new RuntimeException(
"invalid operator: " + _op);
}
}
}
}
Listing 3:
CalculatorCompiler.javaimport java.util.Stack;
import java.util.StringTokenizer;
import java.io.*;
public class CalculatorCompiler
extends ClassLoader
{
String _compiler;
String _classpath;
public CalculatorCompiler() {
super(ClassLoader.getSystemClassLoader());
_compiler = System.getProperty(
"calc.compiler");
if (_compiler == null) {
_compiler = "javac";
}
_classpath = ".";
String extraclasspath =
System.getProperty("calc.classpath");
if (extraclasspath != null) {
_classpath = _classpath +
System.getProperty("path.separator") +
extraclasspath;
}
}
public Calculator compile(String expression)
{
String jtext = javaExpression(expression);
String filename = "";
String classname = "";
try {
File javafile = File.createTempFile(
"compiled_", ".java", new File("."));
filename = javafile.getName();
classname = filename.substring(0,
filename.lastIndexOf("."));
generateJavaFile(javafile, classname,
expression);
invokeCompiler(javafile);
byte[] buf = readBytes(classname + ".class");
Class c = defineClass(buf, 0, buf.length);
try {
return (Calculator) c.newInstance();
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
} catch (InstantiationException e) {
throw new RuntimeException(e.getMessage());
}
} catch(IOException e) {
throw new RuntimeException(e.getMessage());
}
}
void generateJavaFile(File javafile,
String classname, String expression)
throws IOException
{
FileOutputStream out =
new FileOutputStream(javafile);
String text = "public class " + classname +
" implements Calculator {" +
" public int evaluate(int[] args) {" +
" " + javaExpression(expression) +
" }" +
"}";
out.write(text.getBytes());
out.close();
}
void invokeCompiler(File javafile)
throws IOException
{
String[] cmd = {_compiler, "-classpath",
_classpath, javafile.getName()};
Process process = Runtime.getRuntime()
.exec(cmd);
try {
process.waitFor();
} catch (InterruptedException e) {
}
int val = process.exitValue();
if (val != 0) {
throw new RuntimeException(
"compile error: " +
"compiler return code is " + val);
}
}
byte[] readBytes(String filename)
throws IOException
{
File classfile = new File(filename);
byte[] buf =
new byte[(int) classfile.length()];
FileInputStream in =
new FileInputStream(classfile);
in.read(buf);
in.close();
String javaExpression(String expression)
{
Stack stack = new Stack();
StringTokenizer toks =
new StringTokenizer(expression);
while (toks.hasMoreTokens()) {
String tok = toks.nextToken();
if (tok.startsWith("$")) {
stack.push("args[" +
Integer.parseInt(tok.substring(1)) +
"]");
} else {
int op = "+-*/".indexOf(tok.charAt(0));
if (op == -1) {
stack.push(tok);
} else {
String arg2 = (String) stack.pop();
String arg1 = (String) stack.pop();
stack.push("(" + arg1 + " " +
tok.charAt(0) + " " + arg2 + ")");
}
}
}
return "return " + (String) stack.pop() + ";";
}
}
Listing 4: CalculatorGenerator.java
import java.io.*;
import java.util.Stack;
import java.util.StringTokenizer;
import de.fub.bytecode.classfile.*;
import de.fub.bytecode.generic.*;
import de.fub.bytecode.Constants;
public class CalculatorGenerator
extends ClassLoader
{
public Calculator generate(String expression)
{
String classname = "Calc_" +
System.currentTimeMillis();
ClassGen classgen = new ClassGen(classname,
"java.lang.Object", "",
Constants.ACC_PUBLIC | Constants.ACC_SUPER,
new String[] {"Calculator"});
classgen.addEmptyConstructor(
Constants.ACC_PUBLIC);
addEvalMethod(classgen, expression);
byte[] data = classgen.getJavaClass()
.getBytes();
Class c = defineClass(data, 0, data.length);
try {
return (Calculator) c.newInstance();
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage());
} catch (InstantiationException e) {
throw new RuntimeException(e.getMessage());
}
}
private void addEvalMethod(ClassGen classgen,
String expression)
{
ConstantPoolGen cp =
classgen.getConstantPool();
InstructionList il = new InstructionList();
StringTokenizer toks =
new StringTokenizer(expression);
int stacksize = 0;
int maxstack = 0;
while (toks.hasMoreTokens()) {
String tok = toks.nextToken();
if (tok.startsWith("$")) {
int varnum = Integer.
parseInt(tok.substring(1));
// load array reference
il.append(InstructionConstants.ALOAD_1);
// load array index
il.append(new PUSH(cp, varnum));
// pull value from array
il.append(InstructionConstants.IALOAD);
} else {
int op = "+-*/".indexOf(tok.charAt(0));
switch(op) {
case -1:
int val = Integer.parseInt(tok);
il.append(new PUSH(cp, val));
break;
case 0:
il.append(InstructionConstants.IADD);
break;
case 1:
il.append(InstructionConstants.ISUB);
break;
case 2:
il.append(InstructionConstants.IMUL);
break;
case 3:
il.append(InstructionConstants.IDIV);
break;
default:
throw new RuntimeException(
"invalid operator");
}
}
}
il.append(InstructionConstants.IRETURN);
MethodGen method = new MethodGen(
Constants.ACC_PUBLIC, Type.INT,
new Type[] {Type.getType("[I")},
new String[] {"args"},
"evaluate", classgen.getClassName(),
il, cp);
method.setMaxStack();
method.setMaxLocals();
classgen.addMethod(method.getMethod());
}
}