michael.eichberg@dhbw.de, Raum 149B
1.0
Arbeitsweise eines Interpreters:
(inpExpr
sei der aktueller Ausdruck des auszuführenden Programms)
Syntaktische Analyse von inpExpr
Überführung von inpExpr
in eine Befehlsfolge der Maschinensprache, oder der Sprache, in der das Interpreterprogramm selbst geschrieben ist (outExpr
)
Ausführung von outExpr
Wiederholung der Schritte (1) bis (3) für die nächste Anweisung.
Wesentlicher Aspekt ist die semantische Korrektheit: Jedem Quellprogramm P1 in A wird genau ein Zielprogramm P2 in B zugeordnet. Das dem Quellprogramm P1 zugeordnete Zielprogramm P2 muss die gleiche Bedeutung (Semantik) wie P1 besitzen.
Mutmaßlich erfunden von Konteradmiral Grace Murray Hopper (1906–1992)
Nach eigener Aussage hat sie den Compiler aus Faulheit erfunden, und weil sie hoffte, dass "Programmierer wieder Mathematiker werden" könnten.
Quellprogramm wird in eine Folge von Worten zerlegt
Testet, ob das Quellprogramm den Syntaxregeln der Quellsprache entspricht. Strukturiert Worte in gültige Sätze.
Testet, ob alle im Quellprogramm benutzten Namen deklariert wurden und ihrem Typ entsprechend verwendet werden, usw.
Zielprogramm wird erzeugt.
D. h. der Quelltext in Sprache A wird meinem spezialisierten Compiler für Sprache A und Zielplattform X in ein ausführbares Programm für X übersetzt.
Performance/Effizienz: Optimale Ausnutzung der jeweiligen Prozessoreigenschaften und hohe Abarbeitungsgeschwindigkeit der übersetzten Programme.
Plattformabhängigkeit: Ein Programm, das in einer höheren Programmiersprache geschrieben ist, kann - bei Verfügbarkeit eines Compilers - auf jeder Maschine laufen.
es lassen sich relativ schnell lauffähige Programmversionen erstellen (Prototyping)
Schnelle Änderbarkeit: geänderte Anweisungen / Deklarationen des Quellprogramms sind sofort ausführbar
Neuübersetzung nicht notwendig
Längere Ausführungszeit
Werden Anweisungen des Quellprogramms k-mal verwendet (z.B. bei Schleifen), werden sie k-mal analysiert und überführt
Bei Zugriffen auf Variablen müssen die zugeordneten Adressen immer wieder bestimmt werden.
Programme einer höheren Sprache werden in eine Assembler-ähnliche Zwischensprache übersetzt.
Der simulierte Hardware-Prozessor nutzt diese Zwischensprache und besitzt einige Software-Register
Die Anweisungen der Zwischensprache nennt man auch Byte-Code.
Die Zwischensprache wird von der Virtuellen Maschine interpretiert.
Eine virtuelle Maschine versteckt die spezifischen Eigenschaften eines konkreten Prozessors. Wir haben somit eine neue Abstraktionsschicht auf der Hardware-Ebene!
Eine VM verdeckt die speziellen Eigenschaften des jeweiligen Prozessortyps und dient somit als Abstraktionsschicht!
Übersetzte Programme einer Sprache laufen auf allen Prozessortypen, für die es einen Byte-Code Interpreter(VM) gibt.
Es wird nur ein Compiler benötigt und die Sprache wird plattformunabhängig.
Natürlich braucht man eine VM pro Prozessortyp und Platform. Aber das ist ein geringerer Aufwand als für jede Sprache einen eigenen Compiler zu schreiben.
Byte-Code Programme sind langsamer als Maschinenprogramme
Just-in-time-compiler (JIT) versuchen diesen Nachteil aufzulösen. Sie Übersetzen den Byte-Code in ein Objekt-Programm für einen speziellen Prozessortyp sobald es geladen wird, oder nach einer gewissen Anzahl an Ausführungen.
Ein Java-Programm kann aus beliebig vielen Klassen bestehen, von denen mindestens eine die main
-Operation besitzen muss (Hauptprogrammklasse).
Aufgaben von main
Objekterzeugung; d. h. der Aufbau einer anfangs minimalen Welt
Aufruf der ersten Operation
Sollte in der Regel keinen weitergehenden Kontrollfluss des Java-Programms enthalten
Der Kontrollfluss wird innerhalb der Objektoperationen realisiert.
main
wird mit Hilfe des Java-Interpreters gestartet und ausgeführt
Programm zur Ausführung des Java-Bytecodes auf dem konkreten Rechner.
Klassen bzw. Methoden werden bei Bedarf in Code der jeweiligen Maschine übersetzt. Ggf. auf verschiedenen Optimierungsstufen.
Stellt einem Java-Programm wichtige Ressourcen zur Verfügung.
Überprüft, ob die geladenen Bytecodes der JVM-Spezifikation entsprechen. Klassen können über das Netz oder aus dem lokalen Dateisystem zur Laufzeit einer Java-Anwendung in das Laufzeitsystem nachgeladen werden
Ein Teil der Sicherheitsmaßnahmen wird durch den Bytecode Verifier realisiert.
Die Eingabe für javac
sind ein oder mehrere Java-Dateien, die jeweils die eine oder mehrere Klassendefinitionen enthalten.
Eine derartige Datei nennt man eine Übersetzungseinheit
Die Ausgabe ist pro Klasse X genau eine Datei X.class, die den Bytecode der Klasse enthält.
RPN Taschenrechner kompilieren/Java Code übersetzen
Stellen Sie sicher, dass Ihr Programm für den RPN Taschenrechner durch die Klasse RPN implementiert wird und diese Klasse in der entsprechenden Java Datei gespeichert ist. Die Klasse RPN soll im Package rpn
sein! Die Klassen für den Stack und die Liste sollen im Package ds
liegen.
Compilieren Sie nur Ihr Program und die benötigten Hilfsklassen in einem Schritt mit Hilfe von javac
. Starten Sie danach Ihr Programm mit Hilfe von java
. Vergessen Sie nicht den vollqualifizierten Namen der RPN Klasse zu verwenden. Stellen Sie auch sicher, dass Sie sich im passen Root-Verzeichnis befinden.
Falls Sie den Code nicht haben, dann können Sie den Code von hier verwenden. Achten Sie darauf die Dateien in den entsprechenden Verzeichnissen zu speichern!
Datei: rpn/RPN.java
1package rpn;
23
import ds.Stack;
45
public class RPN {
67
static void printAll(Stack<String> stack) {
8for (int i = 0; i < stack.size(); i++) {
9System.out.print(stack.get(i) + " ");
10}
11System.out.println();
12}
1314
public static void main(String[] args) {
15if (args.length == 0) {
16System.out.println("Usage: java RPN <expr>");
17return;
18}
19// Main logic
20Stack<String> infix = new Stack<>();
21Stack<Double> ops = new Stack<>();
22for (String arg : args) {
23switch (arg) {
24case "+":
25ops.push(ops.pop() + ops.pop());
26infix.push("(" + infix.pop() + " + " + infix.pop() + ")");
27break;
28case "*":
29ops.push(ops.pop() * ops.pop());
30infix.push("(" + infix.pop() + " * " + infix.pop() + ")");
31break;
32default:
33infix.push(arg);
34ops.push(Double.parseDouble(arg));
35}
36}
37System.out.println(infix.peek() + " = " + ops.peek());
3839
printAll(infix);
40}
41}
Datei: ds/Stack.java
1package ds;
23
import java.util.NoSuchElementException;
45
public class Stack<T> extends List<T> {
67
public void push(T element) {
8add(element);
9}
1011
public T pop() {
12if (size() == 0) {
13throw new NoSuchElementException();
14}
15T element = get(size() - 1);
16remove(size() - 1);
17return element;
18}
1920
public T peek() {
21if (size() == 0) {
22throw new NoSuchElementException();
23}
24return get(size() - 1);
25}
26}
Datei: ds/List.java
1package ds;
23
import static java.lang.System.arraycopy;
45
public class List<T> {
67
private T[] elements;
8private int count;
910
public List(int size) {
11@SuppressWarnings("unchecked")
12var tElements = (T[]) new Object[Math.max(size, 16)];
13elements = tElements;
14count = 0;
15}
1617
public List() {
18this(16);
19}
2021
public int size() {
22return count;
23}
2425
public T get(int index) {
26if (index < 0 || index >= count) {
27throw new IndexOutOfBoundsException();
28}
29return elements[index];
30}
3132
public void add(T element) {
33if (count == elements.length) {
34Object[] newElements = new Object[Math.min(elements.length * 2, 1000)];
35System.arraycopy(elements, 0, newElements, 0, elements.length);
36@SuppressWarnings("unchecked")
37var tElements = (T[]) newElements;
38elements = tElements;
39}
40elements[count++] = element;
41}
4243
@SafeVarargs
44final public void addAll(T... elements) {
45for (T element : elements) {
46add(element);
47}
48}
4950
public void set(int index, T element) {
51if (index < 0 || index >= count) {
52throw new IndexOutOfBoundsException();
53}
54elements[index] = element;
55}
5657
public String toString() {
58StringBuilder sb = new StringBuilder();
59sb.append("[");
60for (int i = 0; i < count; i++) {
61if (i > 0) {
62sb.append(", ");
63}
64sb.append(elements[i]);
65}
66sb.append("]");
67return sb.toString();
68}
6970
public void remove(int index) {
71if (index < 0 || index >= count) {
72throw new IndexOutOfBoundsException();
73}
7475
if (count < elements.length / 4 && elements.length > 16) {
76@SuppressWarnings("unchecked")
77T[] newElements = (T[]) new Object[Math.max(elements.length / 2, 16)];
78arraycopy(elements, 0, newElements, 0, index);
79arraycopy(elements, index + 1, newElements, index, count - index - 1);
80elements = newElements;
81count--;
82} else {
83arraycopy(elements, index + 1, elements, index, count - index - 1);
84count--;
85}
86}
8788
}