package eu.wietsevenema.lang.oberon.interpreter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import eu.wietsevenema.lang.oberon.ast.declarations.FormalVar;
import eu.wietsevenema.lang.oberon.ast.declarations.FormalVarRef;
import eu.wietsevenema.lang.oberon.ast.expressions.Identifier;
import eu.wietsevenema.lang.oberon.ast.types.BooleanType;
import eu.wietsevenema.lang.oberon.ast.types.IntegerType;
import eu.wietsevenema.lang.oberon.ast.types.VarType;
import eu.wietsevenema.lang.oberon.exceptions.ImmutableException;
import eu.wietsevenema.lang.oberon.exceptions.SymbolAlreadyDeclaredException;
import eu.wietsevenema.lang.oberon.exceptions.TypeMismatchException;
import eu.wietsevenema.lang.oberon.exceptions.SymbolNotDeclaredException;
import eu.wietsevenema.lang.oberon.interpreter.values.IntegerValue;
import eu.wietsevenema.lang.oberon.interpreter.values.Value;
public class BuiltIns {
public static void inject(Environment environment,Scope scope) throws SymbolAlreadyDeclaredException {
if (environment != null) {
scope.declareProc("Write", new Write(environment));
scope.declareProc("WriteLn", new WriteLn(environment));
scope.declareProc("Read", new Read(environment));
}
scope.declareProc("AssertEqualsInt", new AssertEquals<IntegerType>(new IntegerType()));
scope.declareProc("AssertEqualsBool", new AssertEquals<BooleanType>(new BooleanType()));
}
public static class AssertEquals<T extends VarType> implements Procedure {
private VarType type;
public AssertEquals(T type) {
this.type = type;
}
@Override
public List<Formal> getFormals() {
ArrayList<Formal> formals = new ArrayList<Formal>();
formals.add(new FormalVar("expected", this.type));
formals.add(new FormalVar("actual", this.type));
return formals;
}
@Override
public void execute(InterpreterScope scope) {
try {
Value expected = scope.lookupValue("expected");
Value actual = scope.lookupValue("actual");
if (expected == null && actual == null) {
return;
}
if (expected != null && expected.equals(actual)) {
return;
}
throw new AssertionError("Expected " + expected + " but got " + actual);
} catch (SymbolNotDeclaredException e) {
throw new IllegalStateException(e);
}
}
}
public static class WriteLn implements Procedure {
private PrintWriter out;
public WriteLn(Environment environment) {
this.out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(environment.getOut())));
}
@Override
public List<Formal> getFormals() {
ArrayList<Formal> formals = new ArrayList<Formal>();
return formals;
}
@Override
public void execute(InterpreterScope scope) {
this.out.println();
this.out.flush();
}
}
public static class Write implements Procedure {
private PrintWriter out;
public Write(Environment environment) {
this.out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(environment.getOut())));
}
@Override
public List<Formal> getFormals() {
ArrayList<Formal> formals = new ArrayList<Formal>();
formals.add(new FormalVar(new Identifier("out"), new IntegerType()));
return formals;
}
@Override
public void execute(InterpreterScope scope) {
try {
Value result = scope.lookupValue("out");
this.out.print(result.toString());
this.out.flush();
} catch (SymbolNotDeclaredException e) {
throw new IllegalStateException(e);
}
}
}
public static class Read implements Procedure {
private BufferedReader in;
public Read(Environment environment) {
this.in = new BufferedReader(new InputStreamReader(environment.getIn()));
}
@Override
public List<Formal> getFormals() {
ArrayList<Formal> formals = new ArrayList<Formal>();
formals.add(new FormalVarRef(new Identifier("in"), new IntegerType()));
return formals;
}
@Override
public void execute(InterpreterScope scope) throws TypeMismatchException, ImmutableException, SymbolNotDeclaredException {
try {
Integer value = Integer.parseInt(this.in.readLine());
ValueReference ref = scope.lookupValueReference("in");
ref.setValue(new IntegerValue(value));
} catch (NumberFormatException e) {
throw new RuntimeException(e); // Ay, unchecked exception, maar
// hier is het wel nodig ivm
// contract van execute.
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}